diff --git a/7DRL.vcxproj b/7DRL.vcxproj
index 667cf56..72d71c6 100644
--- a/7DRL.vcxproj
+++ b/7DRL.vcxproj
@@ -69,7 +69,9 @@
-
+
+ $(IncludePath)
+
Level3
@@ -84,7 +86,13 @@
Disabled
true
true
+ C:\Users\Usuario\Source\Repos\7DRL\src\soloud\include;D:\Programming\SDL2\include;D:\Programming\libtcod-1.15.1-x86_64-msvc\include
+ WITH_SDL2;_CRT_SECURE_NO_WARNINGS;_MBCS;%(PreprocessorDefinitions)
+
+ SDL2.lib;libtcod.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ D:\Programming\SDL2\lib\x64;D:\Programming\libtcod-1.15.1-x86_64-msvc
+
@@ -108,13 +116,106 @@
true
true
true
+ D:\Programming\libtcod-1.15.1-x86_64-msvc\include;
true
true
+ D:\Programming\SDL2\lib\x64;D:\Programming\libtcod-1.15.1-x86_64-msvc;%(AdditionalLibraryDirectories)
+ SDL2.lib;libtcod.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/7DRL.vcxproj.filters b/7DRL.vcxproj.filters
index 4863ddb..78dd21e 100644
--- a/7DRL.vcxproj.filters
+++ b/7DRL.vcxproj.filters
@@ -14,4 +14,264 @@
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
\ No newline at end of file
diff --git a/SDL2.dll b/SDL2.dll
new file mode 100644
index 0000000..6cf858c
Binary files /dev/null and b/SDL2.dll differ
diff --git a/click.wav b/click.wav
new file mode 100644
index 0000000..833ee01
Binary files /dev/null and b/click.wav differ
diff --git a/libtcod.dll b/libtcod.dll
new file mode 100644
index 0000000..c52972e
Binary files /dev/null and b/libtcod.dll differ
diff --git a/ping.wav b/ping.wav
new file mode 100644
index 0000000..a8fcbec
Binary files /dev/null and b/ping.wav differ
diff --git a/rc11.png b/rc11.png
new file mode 100644
index 0000000..320e4f6
Binary files /dev/null and b/rc11.png differ
diff --git a/src/Drawing.h b/src/Drawing.h
new file mode 100644
index 0000000..6657dfe
--- /dev/null
+++ b/src/Drawing.h
@@ -0,0 +1,114 @@
+#pragma once
+#include "libtcod.h"
+#include "soloud_wav.h"
+#include "defines.h"
+
+static SoLoud::Wav* click = nullptr;
+
+class Drawing
+{
+public:
+
+
+ static void draw_window(TCODConsole* target, int x0, int y0, int w, int h, const std::string& title,
+ bool clear_inside = false)
+ {
+ TCODConsole::root->setDefaultForeground(TCODColor::grey);
+
+ for (int x = x0; x < x0 + w; x++)
+ {
+ target->putChar(x, y0, TCOD_CHAR_DHLINE);
+ target->putChar(x, y0 + h, TCOD_CHAR_DHLINE);
+ }
+
+ for (int y = y0; y < y0 + h; y++)
+ {
+ target->putChar(x0, y, TCOD_CHAR_DVLINE);
+ target->putChar(x0 + w, y, TCOD_CHAR_DVLINE);
+ }
+
+ target->putChar(x0, y0, TCOD_CHAR_DNW);
+ target->putChar(x0 + w, y0, TCOD_CHAR_DNE);
+ target->putChar(x0, y0 + h, TCOD_CHAR_DSW);
+ target->putChar(x0 + w, y0 + h, TCOD_CHAR_DSE);
+
+ int tw = title.size();
+ int tx = x0 + (w - tw) / 2;
+
+ target->putChar(tx - 1, y0, 185);
+ target->putChar(tx + tw, y0, 204);
+
+ TCODConsole::root->setDefaultForeground(TCODColor::white);
+ target->printf(tx, y0, title.c_str());
+
+ if (clear_inside)
+ {
+ for (int x = x0 + 1; x < x0 + w; x++)
+ {
+ for (int y = y0 + 1; y < y0 + h; y++)
+ {
+ target->setChar(x, y, ' ');
+ target->setCharForeground(x, y, TCODColor::white);
+ target->setCharBackground(x, y, TCODColor::black);
+ }
+ }
+ }
+ }
+
+ // Only changes foregrounds
+ // Returns true if changed
+ static bool draw_switch(TCODConsole* target, int x, int y, int rx, int ry, bool* on, const std::string& title)
+ {
+ if (click == nullptr)
+ {
+ click = new SoLoud::Wav();
+ click->load("click.wav");
+ }
+ // LED
+ target->setChar(x, y, '(');
+ target->setChar(x+2, y, ')');
+ target->setChar(x + 1, y, 254);
+
+ // Indicators
+ target->setChar(x, y + 2, '0');
+ target->setChar(x + 2, y + 2, '1');
+
+ // Lever
+ target->setChar(x, y + 1, TCOD_CHAR_DHLINE);
+ target->setChar(x + 1, y + 1, TCOD_CHAR_DHLINE);
+ target->setChar(x + 2, y + 1, TCOD_CHAR_DHLINE);
+
+ if (*on)
+ {
+ target->setChar(x + 2, y + 1, 'O');
+ }
+ else
+ {
+ target->setChar(x, y + 1, 'O');
+ }
+
+ if (*on)
+ {
+ target->setCharForeground(x + 1, y, TCODColor::lightRed);
+ }
+ else
+ {
+ target->setCharForeground(x + 1, y, TCODColor::grey);
+ }
+
+ int tw = title.size();
+ int tx = (x + 1) - (tw / 2);
+ target->printf(tx, y - 1, title.c_str());
+
+ TCOD_mouse_t pos = TCODMouse::getStatus();
+
+ if (pos.cx - rx >= x && pos.cx - rx <= x + 2 && pos.cy - ry == y + 1 && pos.lbutton_pressed)
+ {
+ *on = !(*on);
+ g_soloud->play(*click);
+ return true;
+ }
+
+ return false;
+ }
+};
\ No newline at end of file
diff --git a/src/Help.h b/src/Help.h
new file mode 100644
index 0000000..26370ae
--- /dev/null
+++ b/src/Help.h
@@ -0,0 +1,27 @@
+#pragma once
+#include
+
+
+class Help
+{
+public:
+
+ static constexpr const char* help_main = "\
+-=Main screen=-\n\n\
+F11 - Open/Close this menu\n\
+ESC - Close Menus\n\
+Space - Pause\n\
+\n\n\
+This window shows contextual help depending on your currently open screen\n\
+To open other screens, click on any of the machines in the submarine. \
+If there is nobody operating the machine, click on a crewmember, rightclick \
+the machine, and select \"move\".\n\n\
+-------------------------------------\n\
+What should I do?\n\n\
+Wait for orders from central command, you will receive them on the radio\n\n\
+-------------------------------------\n\
+How can I interact with the crew?\n\n\
+All orders are done via rightclicking, you can find further help on the \"Crewmember\" menu \
+that can be opened on the rightclick dropdown";
+};
+
diff --git a/src/Main.cpp b/src/Main.cpp
new file mode 100644
index 0000000..24ab4c6
--- /dev/null
+++ b/src/Main.cpp
@@ -0,0 +1,49 @@
+#include "libtcod.hpp"
+#include "defines.h"
+
+#include "Status.h"
+#include "flight/FlightScene.h"
+
+#include "soloud/include/soloud.h"
+#include "soloud/include/soloud_wav.h"
+
+SoLoud::Soloud* g_soloud;
+
+int main(int argc, char** argv)
+{
+
+ TCODConsole::setCustomFont("rc11.png", TCOD_FONT_LAYOUT_ASCII_INROW);
+ TCODConsole::initRoot(WIDTH, HEIGHT, "7DRL", false, TCOD_RENDERER_GLSL);
+
+
+ g_soloud = new SoLoud::Soloud;
+ g_soloud->init();
+
+ Status status;
+ FlightScene flight_scene(&status);
+
+ TCODSystem::setFps(60);
+
+ while (!TCODConsole::isWindowClosed())
+ {
+ TCOD_key_t key;
+ TCOD_mouse_t mouse;
+ TCODSystem::checkForEvent(TCOD_EVENT_KEY_PRESS | TCOD_EVENT_MOUSE, &key, &mouse);
+ if (key.vk != TCODK_NONE)
+ {
+ flight_scene.turn(key);
+ }
+
+ flight_scene.update(TCODSystem::getLastFrameLength());
+
+
+
+ TCODConsole::root->clear();
+ flight_scene.render();
+ TCODConsole::flush();
+ }
+
+ g_soloud->deinit();
+
+ return 0;
+}
diff --git a/src/Status.cpp b/src/Status.cpp
new file mode 100644
index 0000000..cdadfa1
--- /dev/null
+++ b/src/Status.cpp
@@ -0,0 +1,27 @@
+#include "Status.h"
+
+
+
+void Status::draw(int x0, int y0, int x1, int y1)
+{
+ int id = 0;
+ for (int y = y1 - 1; y > y0; y--)
+ {
+ int i = strings.size() - id - 1;
+ if (i >= 0 && strings.size() > 0)
+ {
+ TCODConsole::root->printf(x0, y, strings[i].c_str());
+ }
+
+ id++;
+ }
+}
+
+Status::Status()
+{
+}
+
+
+Status::~Status()
+{
+}
diff --git a/src/Status.h b/src/Status.h
new file mode 100644
index 0000000..9e0b535
--- /dev/null
+++ b/src/Status.h
@@ -0,0 +1,17 @@
+#pragma once
+#include
+#include
+
+class Status
+{
+public:
+
+ std::vector strings;
+
+ // x1 is ignored, we are always full width
+ void draw(int x0, int y0, int x1, int y1);
+
+ Status();
+ ~Status();
+};
+
diff --git a/src/defines.h b/src/defines.h
new file mode 100644
index 0000000..b070e19
--- /dev/null
+++ b/src/defines.h
@@ -0,0 +1,7 @@
+#pragma once
+#include "soloud.h"
+
+#define WIDTH 90
+#define HEIGHT 70
+
+extern SoLoud::Soloud* g_soloud;
\ No newline at end of file
diff --git a/src/flight/FlightMap.cpp b/src/flight/FlightMap.cpp
new file mode 100644
index 0000000..47b7c8c
--- /dev/null
+++ b/src/flight/FlightMap.cpp
@@ -0,0 +1,100 @@
+#include "FlightMap.h"
+
+
+
+
+FlightMap::FlightMap(int width, int height, size_t seed) : vmap(width, height)
+{
+ this->width = width;
+ this->height = height;
+
+ tiles.resize(width * height);
+
+ // Cavern style map
+ for (int x = 0; x < width; x++)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ tiles[y * width + x].wall = true;
+ }
+ }
+
+ TCODRandom rng = TCODRandom(seed);
+
+ int dx = width / 2, dy = height / 2;
+ int open = 0;
+ int target_open = rng.getFloat(0.3, 0.5) * width * height;
+ int max_steps = 100000;
+ int steps = 0;
+
+ while (open < target_open && steps < max_steps)
+ {
+ if (tiles[dy * width + dx].wall)
+ {
+ tiles[dy * width + dx].wall = false;
+ open++;
+ }
+
+ if (rng.getFloat(0.0, 1.0) >= 0.5)
+ {
+ if (rng.getFloat(0.0, 1.0) >= 0.5)
+ {
+ dx++;
+ }
+ else
+ {
+ dx--;
+ }
+ }
+ else
+ {
+ if (rng.getFloat(0.0, 1.0) >= 0.5)
+ {
+ dy++;
+ }
+ else
+ {
+ dy--;
+ }
+ }
+
+ if (dx < 1)
+ {
+ dx = 1;
+ }
+
+ if (dx >= width - 1)
+ {
+ dx = width - 2;
+ }
+
+ if (dy < 1)
+ {
+ dy = 1;
+ }
+
+ if (dy >= height - 1)
+ {
+ dy = height - 2;
+ }
+
+
+
+ steps++;
+ }
+
+ // TODO: Generate some bases or tunnels
+
+ // Generate the visibility map
+ for (int x = 0; x < width; x++)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ vmap.setProperties(x, y, !tiles[y * width + x].wall, !tiles[y * width + x].wall);
+ }
+ }
+}
+
+FlightMap::~FlightMap()
+{
+}
diff --git a/src/flight/FlightMap.h b/src/flight/FlightMap.h
new file mode 100644
index 0000000..5c9f52a
--- /dev/null
+++ b/src/flight/FlightMap.h
@@ -0,0 +1,22 @@
+#pragma once
+#include
+#include
+
+struct MapTile
+{
+ bool wall;
+};
+
+class FlightMap
+{
+public:
+
+ int width, height;
+ std::vector tiles;
+
+ TCODMap vmap;
+
+ FlightMap(int width, int height, size_t seed);
+ ~FlightMap();
+};
+
diff --git a/src/flight/FlightScene.cpp b/src/flight/FlightScene.cpp
new file mode 100644
index 0000000..201442f
--- /dev/null
+++ b/src/flight/FlightScene.cpp
@@ -0,0 +1,115 @@
+#include "FlightScene.h"
+
+
+
+void FlightScene::turn(TCOD_key_t key)
+{
+
+ if (open_window == SONAR)
+ {
+
+ if (key.vk == TCODK_UP)
+ {
+ py--;
+ }
+
+ if (key.vk == TCODK_DOWN)
+ {
+ py++;
+ }
+
+ if (key.vk == TCODK_RIGHT)
+ {
+ px++;
+ }
+
+ if (key.vk == TCODK_LEFT)
+ {
+ px--;
+ }
+
+ sonar.draw_world(px, py, map);
+ }
+
+ if (key.vk == TCODK_F11)
+ {
+ help_open = !help_open;
+ }
+}
+
+void FlightScene::update(float dt)
+{
+ sonar.update(dt);
+}
+
+void FlightScene::render()
+{
+ TCODConsole::blit(&spaceship, 0, 0,
+ spaceship.getWidth(), spaceship.getHeight(), TCODConsole::root, sonar.console.getWidth(), 0);
+
+
+
+ status->draw(1, sonar.console.getHeight(), WIDTH - 1, HEIGHT - 1);
+
+ // Draw frames
+ for (int i = 0; i < WIDTH; i++)
+ {
+ TCODConsole::root->setChar(i, 49, TCOD_CHAR_DHLINE);
+ }
+
+ if (open_window == SONAR)
+ {
+
+ int x0 = (TCODConsole::root->getWidth() - sonar.console.getWidth()) / 2;
+ int y0 = (TCODConsole::root->getHeight() - sonar.console.getHeight()) / 2;
+
+ sonar.draw(x0, y0);
+
+
+ Drawing::draw_window(TCODConsole::root, x0 - 1, y0 - 1, sonar.console.getWidth(), sonar.console.getHeight(), "Sonar");
+
+ TCODConsole::blit(&sonar.console, 0, 0,
+ sonar.console.getWidth() - 1, sonar.console.getHeight() - 1, TCODConsole::root, x0, y0);
+ }
+
+ if (help_open)
+ {
+ Drawing::draw_window(TCODConsole::root, 0, 0, 40, HEIGHT - 1, "Help", true);
+
+ TCODConsole::root->setDefaultForeground(TCODColor::lightGrey);
+ TCODConsole::root->printRect(1, 1, 39, HEIGHT - 2, help_str.c_str());
+ }
+
+
+
+}
+
+
+FlightScene::FlightScene(Status* status) :
+ sonar(soloud), spaceship(WIDTH - 49, 49), map(map_size, map_size, 11234)
+{
+ underwater.load("underwater.wav");
+ underwater.setLooping(true);
+
+ this->soloud = g_soloud;
+ this->status = status;
+
+ px = map_size / 2;
+ py = map_size / 2;
+
+ sonar.sonar_active = true;
+ sonar.draw_world(px, py, map);
+
+
+
+ soloud->play(underwater);
+
+ open_window = SONAR;
+
+ help_str = Help::help_main;
+}
+
+
+FlightScene::~FlightScene()
+{
+}
diff --git a/src/flight/FlightScene.h b/src/flight/FlightScene.h
new file mode 100644
index 0000000..b2f47f9
--- /dev/null
+++ b/src/flight/FlightScene.h
@@ -0,0 +1,52 @@
+#pragma once
+#include
+#include "../defines.h"
+#include "../Status.h"
+#include "FlightMap.h"
+#include "soloud.h"
+#include "soloud_wav.h"
+#include "Sonar.h"
+#include "../Drawing.h"
+#include "../Help.h"
+
+
+constexpr int map_size = 512;
+
+enum Window
+{
+ NONE,
+ SONAR
+};
+
+class FlightScene
+{
+public:
+
+ SoLoud::Soloud* soloud;
+
+ SoLoud::Wav underwater;
+
+ FlightMap map;
+
+ Window open_window;
+
+
+ Sonar sonar;
+
+ bool help_open;
+ std::string help_str;
+
+ int px, py;
+
+
+ TCODConsole spaceship;
+ Status* status;
+
+ void turn(TCOD_key_t key);
+ void update(float dt);
+ void render();
+
+ FlightScene(Status* status);
+ ~FlightScene();
+};
+
diff --git a/src/flight/Sonar.cpp b/src/flight/Sonar.cpp
new file mode 100644
index 0000000..d0d21f2
--- /dev/null
+++ b/src/flight/Sonar.cpp
@@ -0,0 +1,127 @@
+#include "Sonar.h"
+
+
+
+void Sonar::update(float dt)
+{
+ sonar_radius += dt * 14.0f;
+
+ if (!sonar_active)
+ {
+ sonar_radius = 0.0f;
+ }
+
+ if (sonar_radius >= 40.0f)
+ {
+ sonar_radius = 0.0f;
+ g_soloud->play(ping);
+ }
+}
+
+void Sonar::draw(int rx, int ry)
+{
+ // Fade old pixels
+ for (int x = 0; x < 49; x++)
+ {
+ for (int y = 0; y < 49; y++)
+ {
+ TCODColor cur = console.getCharForeground(x, y);
+
+ cur.setValue(cur.getValue() * 0.984f);
+
+
+ console.setCharForeground(x, y, cur);
+ }
+ }
+
+
+
+ for (int x = 0; x < 49; x++)
+ {
+ for (int y = 0; y < 49; y++)
+ {
+ float xf = (float)(x - 24.0f);
+ float yf = (float)(y - 24.0f);
+ float dist = sqrt(xf * xf + yf * yf);
+ if (abs(dist - sonar_radius) <= 2.0f)
+ {
+ console.setCharForeground(x, y, TCODColor(80, 255, 80));
+ }
+ }
+ }
+
+
+
+ console.setCharForeground(24, 24, TCODColor(255, 255, 255));
+ console.setChar(24, 24, 127);
+
+ if (Drawing::draw_switch(&console, 8, 51, rx, ry, &sonar_active, "Active Sonar") && sonar_active)
+ {
+ g_soloud->play(ping);
+ }
+}
+
+void Sonar::draw_world(int px, int py, FlightMap& map)
+{
+ // Run a visibility algorithm and draw
+ map.vmap.computeFov(px, py, sonar_active ? 24 : 4, true, FOV_BASIC);
+
+ for (int x = -24; x < 24; x++)
+ {
+ for (int y = -24; y < 24; y++)
+ {
+ int mx = x + px;
+ int my = y + py;
+
+ int sx = x + 24;
+ int sy = y + 24;
+
+ bool seen = true;
+
+ if (mx < 0 || my < 0 || mx >= map.width || my >= map.height)
+ {
+ seen = false;
+ }
+
+ if (!map.vmap.isInFov(mx, my))
+ {
+ seen = false;
+ }
+
+ if (seen)
+ {
+ if (map.tiles[my * map.width + mx].wall)
+ {
+ //console.setChar(sx, sy, 219); // Block
+ console.setChar(sx, sy, 178);
+ }
+ else
+ {
+ //radar.setChar(sx, sy, 249);
+ console.setChar(sx, sy, 176); // Dust style
+ }
+
+ }
+ else
+ {
+ console.setChar(sx, sy, 250);
+ //radar.setChar(sx, sy, ' ');
+ }
+
+ }
+ }
+}
+
+
+
+Sonar::Sonar(SoLoud::Soloud * soloud) : console(49, 56)
+{
+ this->soloud = soloud;
+ ping.load("ping.wav");
+}
+
+Sonar::~Sonar()
+{
+
+}
+
diff --git a/src/flight/Sonar.h b/src/flight/Sonar.h
new file mode 100644
index 0000000..3a8202b
--- /dev/null
+++ b/src/flight/Sonar.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "libtcod.h"
+#include "soloud.h"
+#include "soloud_wav.h"
+
+#include "FlightMap.h"
+#include "../Drawing.h"
+
+class Sonar
+{
+public:
+
+ SoLoud::Soloud* soloud;
+
+ TCODConsole console;
+ float sonar_radius;
+ bool sonar_active;
+ SoLoud::Wav ping;
+
+ void update(float dt);
+ void draw(int rx, int ry);
+ void draw_world(int px, int py, FlightMap& map);
+
+ Sonar(SoLoud::Soloud* soloud);
+ ~Sonar();
+};
+
diff --git a/src/soloud/include/soloud.h b/src/soloud/include/soloud.h
new file mode 100644
index 0000000..c759ee4
--- /dev/null
+++ b/src/soloud/include/soloud.h
@@ -0,0 +1,546 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_H
+#define SOLOUD_H
+
+#include // rand
+#include // sin
+
+#ifdef SOLOUD_NO_ASSERTS
+#define SOLOUD_ASSERT(x)
+#else
+#ifdef _MSC_VER
+#include // for sprintf in asserts
+#define VC_EXTRALEAN
+#define WIN32_LEAN_AND_MEAN
+#include // only needed for OutputDebugStringA, should be solved somehow.
+#define SOLOUD_ASSERT(x) if (!(x)) { char temp[200]; sprintf(temp, "%s(%d): assert(%s) failed.\n", __FILE__, __LINE__, #x); OutputDebugStringA(temp); __debugbreak(); }
+#else
+#include // assert
+#define SOLOUD_ASSERT(x) assert(x)
+#endif
+#endif
+
+#ifdef WITH_SDL
+#undef WITH_SDL2
+#undef WITH_SDL1
+#define WITH_SDL1
+#define WITH_SDL2
+#endif
+
+#ifdef WITH_SDL_STATIC
+#undef WITH_SDL1_STATIC
+#define WITH_SDL1_STATIC
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265359
+#endif
+
+#if defined(_WIN32)||defined(_WIN64)
+#define WINDOWS_VERSION
+#endif
+
+#if !defined(DISABLE_SIMD)
+#if defined(__x86_64__) || defined( _M_X64 ) || defined( __i386 ) || defined( _M_IX86 )
+#define SOLOUD_SSE_INTRINSICS
+#endif
+#endif
+
+#define SOLOUD_VERSION 201811
+
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+// Configuration defines
+
+// Maximum number of filters per stream
+#define FILTERS_PER_STREAM 8
+
+// Number of samples to process on one go
+#define SAMPLE_GRANULARITY 512
+
+// Maximum number of concurrent voices (hard limit is 4095)
+#define VOICE_COUNT 1024
+
+// Use linear resampler
+#define RESAMPLER_LINEAR
+
+// 1)mono, 2)stereo 4)quad 6)5.1 8)7.1
+#define MAX_CHANNELS 8
+
+//
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+
+// Typedefs have to be made before the includes, as the
+// includes depend on them.
+namespace SoLoud
+{
+ class Soloud;
+ typedef void (*mutexCallFunction)(void *aMutexPtr);
+ typedef void (*soloudCallFunction)(Soloud *aSoloud);
+ typedef unsigned int result;
+ typedef unsigned int handle;
+ typedef double time;
+};
+
+namespace SoLoud
+{
+ // Class that handles aligned allocations to support vectorized operations
+ class AlignedFloatBuffer
+ {
+ public:
+ float *mData; // aligned pointer
+ unsigned char *mBasePtr; // raw allocated pointer (for delete)
+ int mFloats; // size of buffer (w/out padding)
+
+ // ctor
+ AlignedFloatBuffer();
+ // Allocate and align buffer
+ result init(unsigned int aFloats);
+ // Clear data to zero.
+ void clear();
+ // dtor
+ ~AlignedFloatBuffer();
+ };
+
+ // Lightweight class that handles small aligned buffer to support vectorized operations
+ class TinyAlignedFloatBuffer
+ {
+ public:
+ float *mData; // aligned pointer
+ unsigned char mActualData[sizeof(float) * 16 + 16];
+
+ // ctor
+ TinyAlignedFloatBuffer();
+ };
+};
+
+#include "soloud_filter.h"
+#include "soloud_fader.h"
+#include "soloud_audiosource.h"
+#include "soloud_bus.h"
+#include "soloud_queue.h"
+#include "soloud_error.h"
+
+namespace SoLoud
+{
+
+ // Soloud core class.
+ class Soloud
+ {
+ public:
+ // Back-end data; content is up to the back-end implementation.
+ void * mBackendData;
+ // Pointer for the audio thread mutex.
+ void * mAudioThreadMutex;
+ // Flag for when we're inside the mutex, used for debugging.
+ bool mInsideAudioThreadMutex;
+ // Called by SoLoud to shut down the back-end. If NULL, not called. Should be set by back-end.
+ soloudCallFunction mBackendCleanupFunc;
+
+ // CTor
+ Soloud();
+ // DTor
+ ~Soloud();
+
+ enum BACKENDS
+ {
+ AUTO = 0,
+ SDL1,
+ SDL2,
+ PORTAUDIO,
+ WINMM,
+ XAUDIO2,
+ WASAPI,
+ ALSA,
+ OSS,
+ OPENAL,
+ COREAUDIO,
+ OPENSLES,
+ VITA_HOMEBREW,
+ NULLDRIVER,
+ BACKEND_MAX,
+ };
+
+ enum FLAGS
+ {
+ // Use round-off clipper
+ CLIP_ROUNDOFF = 1,
+ ENABLE_VISUALIZATION = 2,
+ LEFT_HANDED_3D = 4
+ };
+
+ // Initialize SoLoud. Must be called before SoLoud can be used.
+ result init(unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aBackend = Soloud::AUTO, unsigned int aSamplerate = Soloud::AUTO, unsigned int aBufferSize = Soloud::AUTO, unsigned int aChannels = 2);
+
+ // Deinitialize SoLoud. Must be called before shutting down.
+ void deinit();
+
+ // Query SoLoud version number (should equal to SOLOUD_VERSION macro)
+ unsigned int getVersion() const;
+
+ // Translate error number to an asciiz string
+ const char * getErrorString(result aErrorCode) const;
+
+ // Returns current backend ID (BACKENDS enum)
+ unsigned int getBackendId();
+ // Returns current backend string. May be NULL.
+ const char * getBackendString();
+ // Returns current backend channel count (1 mono, 2 stereo, etc)
+ unsigned int getBackendChannels();
+ // Returns current backend sample rate
+ unsigned int getBackendSamplerate();
+ // Returns current backend buffer size
+ unsigned int getBackendBufferSize();
+
+ // Set speaker position in 3d space
+ result setSpeakerPosition(unsigned int aChannel, float aX, float aY, float aZ);
+ // Get speaker position in 3d space
+ result getSpeakerPosition(unsigned int aChannel, float &aX, float &aY, float &aZ);
+
+ // Start playing a sound. Returns voice handle, which can be ignored or used to alter the playing sound's parameters. Negative volume means to use default.
+ handle play(AudioSource &aSound, float aVolume = -1.0f, float aPan = 0.0f, bool aPaused = 0, unsigned int aBus = 0);
+ // Start playing a sound delayed in relation to other sounds called via this function. Negative volume means to use default.
+ handle playClocked(time aSoundTime, AudioSource &aSound, float aVolume = -1.0f, float aPan = 0.0f, unsigned int aBus = 0);
+ // Start playing a 3d audio source
+ handle play3d(AudioSource &aSound, float aPosX, float aPosY, float aPosZ, float aVelX = 0.0f, float aVelY = 0.0f, float aVelZ = 0.0f, float aVolume = 1.0f, bool aPaused = 0, unsigned int aBus = 0);
+ // Start playing a 3d audio source, delayed in relation to other sounds called via this function.
+ handle play3dClocked(time aSoundTime, AudioSource &aSound, float aPosX, float aPosY, float aPosZ, float aVelX = 0.0f, float aVelY = 0.0f, float aVelZ = 0.0f, float aVolume = 1.0f, unsigned int aBus = 0);
+ // Start playing a sound without any panning. It will be played at full volume.
+ handle playBackground(AudioSource &aSound, float aVolume = -1.0f, bool aPaused = 0, unsigned int aBus = 0);
+
+ // Seek the audio stream to certain point in time. Some streams can't seek backwards. Relative play speed affects time.
+ result seek(handle aVoiceHandle, time aSeconds);
+ // Stop the sound.
+ void stop(handle aVoiceHandle);
+ // Stop all voices.
+ void stopAll();
+ // Stop all voices that play this sound source
+ void stopAudioSource(AudioSource &aSound);
+ // Count voices that play this audio source
+ int countAudioSource(AudioSource &aSound);
+
+ // Set a live filter parameter. Use 0 for the global filters.
+ void setFilterParameter(handle aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aValue);
+ // Get a live filter parameter. Use 0 for the global filters.
+ float getFilterParameter(handle aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId);
+ // Fade a live filter parameter. Use 0 for the global filters.
+ void fadeFilterParameter(handle aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aTo, time aTime);
+ // Oscillate a live filter parameter. Use 0 for the global filters.
+ void oscillateFilterParameter(handle aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aFrom, float aTo, time aTime);
+
+ // Get current play time, in seconds.
+ time getStreamTime(handle aVoiceHandle);
+ // Get current sample position, in seconds.
+ time getStreamPosition(handle aVoiceHandle);
+ // Get current pause state.
+ bool getPause(handle aVoiceHandle);
+ // Get current volume.
+ float getVolume(handle aVoiceHandle);
+ // Get current overall volume (set volume * 3d volume)
+ float getOverallVolume(handle aVoiceHandle);
+ // Get current pan.
+ float getPan(handle aVoiceHandle);
+ // Get current sample rate.
+ float getSamplerate(handle aVoiceHandle);
+ // Get current voice protection state.
+ bool getProtectVoice(handle aVoiceHandle);
+ // Get the current number of busy voices.
+ unsigned int getActiveVoiceCount();
+ // Get the current number of voices in SoLoud
+ unsigned int getVoiceCount();
+ // Check if the handle is still valid, or if the sound has stopped.
+ bool isValidVoiceHandle(handle aVoiceHandle);
+ // Get current relative play speed.
+ float getRelativePlaySpeed(handle aVoiceHandle);
+ // Get current post-clip scaler value.
+ float getPostClipScaler() const;
+ // Get current global volume
+ float getGlobalVolume() const;
+ // Get current maximum active voice setting
+ unsigned int getMaxActiveVoiceCount() const;
+ // Query whether a voice is set to loop.
+ bool getLooping(handle aVoiceHandle);
+ // Get voice loop point value
+ time getLoopPoint(handle aVoiceHandle);
+
+ // Set voice loop point value
+ void setLoopPoint(handle aVoiceHandle, time aLoopPoint);
+ // Set voice's loop state
+ void setLooping(handle aVoiceHandle, bool aLooping);
+ // Set current maximum active voice setting
+ result setMaxActiveVoiceCount(unsigned int aVoiceCount);
+ // Set behavior for inaudible sounds
+ void setInaudibleBehavior(handle aVoiceHandle, bool aMustTick, bool aKill);
+ // Set the global volume
+ void setGlobalVolume(float aVolume);
+ // Set the post clip scaler value
+ void setPostClipScaler(float aScaler);
+ // Set the pause state
+ void setPause(handle aVoiceHandle, bool aPause);
+ // Pause all voices
+ void setPauseAll(bool aPause);
+ // Set the relative play speed
+ result setRelativePlaySpeed(handle aVoiceHandle, float aSpeed);
+ // Set the voice protection state
+ void setProtectVoice(handle aVoiceHandle, bool aProtect);
+ // Set the sample rate
+ void setSamplerate(handle aVoiceHandle, float aSamplerate);
+ // Set panning value; -1 is left, 0 is center, 1 is right
+ void setPan(handle aVoiceHandle, float aPan);
+ // Set absolute left/right volumes
+ void setPanAbsolute(handle aVoiceHandle, float aLVolume, float aRVolume, float aLBVolume = 0, float aRBVolume = 0, float aCVolume = 0, float aSVolume = 0);
+ // Set overall volume
+ void setVolume(handle aVoiceHandle, float aVolume);
+ // Set delay, in samples, before starting to play samples. Calling this on a live sound will cause glitches.
+ void setDelaySamples(handle aVoiceHandle, unsigned int aSamples);
+
+ // Set up volume fader
+ void fadeVolume(handle aVoiceHandle, float aTo, time aTime);
+ // Set up panning fader
+ void fadePan(handle aVoiceHandle, float aTo, time aTime);
+ // Set up relative play speed fader
+ void fadeRelativePlaySpeed(handle aVoiceHandle, float aTo, time aTime);
+ // Set up global volume fader
+ void fadeGlobalVolume(float aTo, time aTime);
+ // Schedule a stream to pause
+ void schedulePause(handle aVoiceHandle, time aTime);
+ // Schedule a stream to stop
+ void scheduleStop(handle aVoiceHandle, time aTime);
+
+ // Set up volume oscillator
+ void oscillateVolume(handle aVoiceHandle, float aFrom, float aTo, time aTime);
+ // Set up panning oscillator
+ void oscillatePan(handle aVoiceHandle, float aFrom, float aTo, time aTime);
+ // Set up relative play speed oscillator
+ void oscillateRelativePlaySpeed(handle aVoiceHandle, float aFrom, float aTo, time aTime);
+ // Set up global volume oscillator
+ void oscillateGlobalVolume(float aFrom, float aTo, time aTime);
+
+ // Set global filters. Set to NULL to clear the filter.
+ void setGlobalFilter(unsigned int aFilterId, Filter *aFilter);
+
+ // Enable or disable visualization data gathering
+ void setVisualizationEnable(bool aEnable);
+
+ // Calculate and get 256 floats of FFT data for visualization. Visualization has to be enabled before use.
+ float *calcFFT();
+
+ // Get 256 floats of wave data for visualization. Visualization has to be enabled before use.
+ float *getWave();
+
+ // Get approximate output volume for a channel for visualization. Visualization has to be enabled before use.
+ float getApproximateVolume(unsigned int aChannel);
+
+ // Get current loop count. Returns 0 if handle is not valid. (All audio sources may not update loop count)
+ unsigned int getLoopCount(handle aVoiceHandle);
+
+ // Get audiosource-specific information from a voice.
+ float getInfo(handle aVoiceHandle, unsigned int aInfoKey);
+
+ // Create a voice group. Returns 0 if unable (out of voice groups / out of memory)
+ handle createVoiceGroup();
+ // Destroy a voice group.
+ result destroyVoiceGroup(handle aVoiceGroupHandle);
+ // Add a voice handle to a voice group
+ result addVoiceToGroup(handle aVoiceGroupHandle, handle aVoiceHandle);
+ // Is this handle a valid voice group?
+ bool isVoiceGroup(handle aVoiceGroupHandle);
+ // Is this voice group empty?
+ bool isVoiceGroupEmpty(handle aVoiceGroupHandle);
+
+ // Perform 3d audio parameter update
+ void update3dAudio();
+
+ // Set the speed of sound constant for doppler
+ result set3dSoundSpeed(float aSpeed);
+ // Get the current speed of sound constant for doppler
+ float get3dSoundSpeed();
+ // Set 3d listener parameters
+ void set3dListenerParameters(float aPosX, float aPosY, float aPosZ, float aAtX, float aAtY, float aAtZ, float aUpX, float aUpY, float aUpZ, float aVelocityX = 0.0f, float aVelocityY = 0.0f, float aVelocityZ = 0.0f);
+ // Set 3d listener position
+ void set3dListenerPosition(float aPosX, float aPosY, float aPosZ);
+ // Set 3d listener "at" vector
+ void set3dListenerAt(float aAtX, float aAtY, float aAtZ);
+ // set 3d listener "up" vector
+ void set3dListenerUp(float aUpX, float aUpY, float aUpZ);
+ // Set 3d listener velocity
+ void set3dListenerVelocity(float aVelocityX, float aVelocityY, float aVelocityZ);
+
+ // Set 3d audio source parameters
+ void set3dSourceParameters(handle aVoiceHandle, float aPosX, float aPosY, float aPosZ, float aVelocityX = 0.0f, float aVelocityY = 0.0f, float aVelocityZ = 0.0f);
+ // Set 3d audio source position
+ void set3dSourcePosition(handle aVoiceHandle, float aPosX, float aPosY, float aPosZ);
+ // Set 3d audio source velocity
+ void set3dSourceVelocity(handle aVoiceHandle, float aVelocityX, float aVelocityY, float aVelocityZ);
+ // Set 3d audio source min/max distance (distance < min means max volume)
+ void set3dSourceMinMaxDistance(handle aVoiceHandle, float aMinDistance, float aMaxDistance);
+ // Set 3d audio source attenuation parameters
+ void set3dSourceAttenuation(handle aVoiceHandle, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+ // Set 3d audio source doppler factor to reduce or enhance doppler effect. Default = 1.0
+ void set3dSourceDopplerFactor(handle aVoiceHandle, float aDopplerFactor);
+
+ // Rest of the stuff is used internally.
+
+ // Returns mixed float samples in buffer. Called by the back-end, or user with null driver.
+ void mix(float *aBuffer, unsigned int aSamples);
+ // Returns mixed 16-bit signed integer samples in buffer. Called by the back-end, or user with null driver.
+ void mixSigned16(short *aBuffer, unsigned int aSamples);
+ public:
+ // Mix N samples * M channels. Called by other mix_ functions.
+ void mix_internal(unsigned int aSamples);
+
+ // Handle rest of initialization (called from backend)
+ void postinit(unsigned int aSamplerate, unsigned int aBufferSize, unsigned int aFlags, unsigned int aChannels);
+
+ // Update list of active voices
+ void calcActiveVoices();
+ // Map resample buffers to active voices
+ void mapResampleBuffers();
+ // Perform mixing for a specific bus
+ void mixBus(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize, float *aScratch, unsigned int aBus, float aSamplerate, unsigned int aChannels);
+ // Max. number of active voices. Busses and tickable inaudibles also count against this.
+ unsigned int mMaxActiveVoices;
+ // Highest voice in use so far
+ unsigned int mHighestVoice;
+ // Scratch buffer, used for resampling.
+ AlignedFloatBuffer mScratch;
+ // Current size of the scratch, in samples.
+ unsigned int mScratchSize;
+ // Amount of scratch needed.
+ unsigned int mScratchNeeded;
+ // Output scratch buffer, used in mix_().
+ AlignedFloatBuffer mOutputScratch;
+ // Resampler buffers, two per active voice.
+ AlignedFloatBuffer *mResampleData;
+ // Owners of the resample data
+ AudioSourceInstance **mResampleDataOwner;
+ // Audio voices.
+ AudioSourceInstance *mVoice[VOICE_COUNT];
+ // Output sample rate (not float)
+ unsigned int mSamplerate;
+ // Output channel count
+ unsigned int mChannels;
+ // Current backend ID
+ unsigned int mBackendID;
+ // Current backend string
+ const char * mBackendString;
+ // Maximum size of output buffer; used to calculate needed scratch.
+ unsigned int mBufferSize;
+ // Flags; see Soloud::FLAGS
+ unsigned int mFlags;
+ // Global volume. Applied before clipping.
+ float mGlobalVolume;
+ // Post-clip scaler. Applied after clipping.
+ float mPostClipScaler;
+ // Current play index. Used to create audio handles.
+ unsigned int mPlayIndex;
+ // Current sound source index. Used to create sound source IDs.
+ unsigned int mAudioSourceID;
+ // Fader for the global volume.
+ Fader mGlobalVolumeFader;
+ // Global stream time, for the global volume fader.
+ time mStreamTime;
+ // Last time seen by the playClocked call
+ time mLastClockedTime;
+ // Global filter
+ Filter *mFilter[FILTERS_PER_STREAM];
+ // Global filter instance
+ FilterInstance *mFilterInstance[FILTERS_PER_STREAM];
+ // Find a free voice, stopping the oldest if no free voice is found.
+ int findFreeVoice();
+ // Converts handle to voice, if the handle is valid. Returns -1 if not.
+ int getVoiceFromHandle(handle aVoiceHandle) const;
+ // Converts voice + playindex into handle
+ handle getHandleFromVoice(unsigned int aVoice) const;
+ // Stop voice (not handle).
+ void stopVoice(unsigned int aVoice);
+ // Set voice (not handle) pan.
+ void setVoicePan(unsigned int aVoice, float aPan);
+ // Set voice (not handle) relative play speed.
+ result setVoiceRelativePlaySpeed(unsigned int aVoice, float aSpeed);
+ // Set voice (not handle) volume.
+ void setVoiceVolume(unsigned int aVoice, float aVolume);
+ // Set voice (not handle) pause state.
+ void setVoicePause(unsigned int aVoice, int aPause);
+ // Update overall volume from set and 3d volumes
+ void updateVoiceVolume(unsigned int aVoice);
+ // Update overall relative play speed from set and 3d speeds
+ void updateVoiceRelativePlaySpeed(unsigned int aVoice);
+ // Perform 3d audio calculation for array of voices
+ void update3dVoices(unsigned int *aVoiceList, unsigned int aVoiceCount);
+ // Clip the samples in the buffer
+ void clip(AlignedFloatBuffer &aBuffer, AlignedFloatBuffer &aDestBuffer, unsigned int aSamples, float aVolume0, float aVolume1);
+ // Approximate volume for channels.
+ float mVisualizationChannelVolume[MAX_CHANNELS];
+ // Mono-mixed wave data for visualization and for visualization FFT input
+ float mVisualizationWaveData[256];
+ // FFT output data
+ float mFFTData[256];
+ // Snapshot of wave data for visualization
+ float mWaveData[256];
+
+ // 3d listener position
+ float m3dPosition[3];
+ // 3d listener look-at
+ float m3dAt[3];
+ // 3d listener up
+ float m3dUp[3];
+ // 3d listener velocity
+ float m3dVelocity[3];
+ // 3d speed of sound (for doppler)
+ float m3dSoundSpeed;
+
+ // 3d position of speakers
+ float m3dSpeakerPosition[3 * MAX_CHANNELS];
+
+ // Data related to 3d processing, separate from AudioSource so we can do 3d calculations without audio mutex.
+ AudioSourceInstance3dData m3dData[VOICE_COUNT];
+
+ // For each voice group, first int is number of ints alocated.
+ unsigned int **mVoiceGroup;
+ unsigned int mVoiceGroupCount;
+
+ // List of currently active voices
+ unsigned int mActiveVoice[VOICE_COUNT];
+ // Number of currently active voices
+ unsigned int mActiveVoiceCount;
+ // Active voices list needs to be recalculated
+ bool mActiveVoiceDirty;
+
+ // Remove all non-active voices from group
+ void trimVoiceGroup(handle aVoiceGroupHandle);
+ // Get pointer to the zero-terminated array of voice handles in a voice group
+ handle * voiceGroupHandleToArray(handle aVoiceGroupHandle) const;
+
+ // Lock audio thread mutex.
+ void lockAudioMutex();
+ // Unlock audio thread mutex.
+ void unlockAudioMutex();
+ };
+};
+
+#endif
diff --git a/src/soloud/include/soloud_audiosource.h b/src/soloud/include/soloud_audiosource.h
new file mode 100644
index 0000000..77e6156
--- /dev/null
+++ b/src/soloud/include/soloud_audiosource.h
@@ -0,0 +1,315 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_AUDIOSOURCE_H
+#define SOLOUD_AUDIOSOURCE_H
+
+#include "soloud.h"
+#include "soloud_fader.h"
+#include "soloud_filter.h"
+
+namespace SoLoud
+{
+ class AudioSource;
+ class AudioSourceInstance;
+ class AudioSourceInstance3dData;
+
+ class AudioCollider
+ {
+ public:
+ // Calculate volume multiplier. Assumed to return value between 0 and 1.
+ virtual float collide(Soloud *aSoloud, AudioSourceInstance3dData *aAudioInstance3dData, int aUserData) = 0;
+ };
+
+ class AudioAttenuator
+ {
+ public:
+ virtual float attenuate(float aDistance, float aMinDistance, float aMaxDistance, float aRolloffFactor) = 0;
+ };
+
+ class AudioSourceInstance3dData
+ {
+ public:
+ // ctor
+ AudioSourceInstance3dData();
+ // Set settings from audiosource
+ void init(AudioSource &aSource);
+ // 3d position
+ float m3dPosition[3];
+ // 3d velocity
+ float m3dVelocity[3];
+ // 3d cone direction
+ /*
+ float m3dConeDirection[3];
+ // 3d cone inner angle
+ float m3dConeInnerAngle;
+ // 3d cone outer angle
+ float m3dConeOuterAngle;
+ // 3d cone outer volume multiplier
+ float m3dConeOuterVolume;
+ */
+ // 3d min distance
+ float m3dMinDistance;
+ // 3d max distance
+ float m3dMaxDistance;
+ // 3d attenuation rolloff factor
+ float m3dAttenuationRolloff;
+ // 3d attenuation model
+ unsigned int m3dAttenuationModel;
+ // 3d doppler factor
+ float m3dDopplerFactor;
+ // Pointer to a custom audio collider object
+ AudioCollider *mCollider;
+ // Pointer to a custom audio attenuator object
+ AudioAttenuator *mAttenuator;
+ // User data related to audio collider
+ int mColliderData;
+
+ // Doppler sample rate multiplier
+ float mDopplerValue;
+ // Overall 3d volume
+ float m3dVolume;
+ // Channel volume
+ float mChannelVolume[MAX_CHANNELS];
+ // Copy of flags
+ unsigned int mFlags;
+ // Latest handle for this voice
+ handle mHandle;
+ };
+
+ // Base class for audio instances
+ class AudioSourceInstance
+ {
+ public:
+ enum FLAGS
+ {
+ // This audio instance loops (if supported)
+ LOOPING = 1,
+ // This audio instance is protected - won't get stopped if we run out of voices
+ PROTECTED = 2,
+ // This audio instance is paused
+ PAUSED = 4,
+ // This audio instance is affected by 3d processing
+ PROCESS_3D = 8,
+ // This audio instance has listener-relative 3d coordinates
+ LISTENER_RELATIVE = 16,
+ // Currently inaudible
+ INAUDIBLE = 32,
+ // If inaudible, should be killed (default = don't kill kill)
+ INAUDIBLE_KILL = 64,
+ // If inaudible, should still be ticked (default = pause)
+ INAUDIBLE_TICK = 128
+ };
+ // Ctor
+ AudioSourceInstance();
+ // Dtor
+ virtual ~AudioSourceInstance();
+ // Play index; used to identify instances from handles
+ unsigned int mPlayIndex;
+ // Loop count
+ unsigned int mLoopCount;
+ // Flags; see AudioSourceInstance::FLAGS
+ unsigned int mFlags;
+ // Pan value, for getPan()
+ float mPan;
+ // Volume for each channel (panning)
+ float mChannelVolume[MAX_CHANNELS];
+ // Set volume
+ float mSetVolume;
+ // Overall volume overall = set * 3d
+ float mOverallVolume;
+ // Base samplerate; samplerate = base samplerate * relative play speed
+ float mBaseSamplerate;
+ // Samplerate; samplerate = base samplerate * relative play speed
+ float mSamplerate;
+ // Number of channels this audio source produces
+ unsigned int mChannels;
+ // Relative play speed; samplerate = base samplerate * relative play speed
+ float mSetRelativePlaySpeed;
+ // Overall relative plays peed; overall = set * 3d
+ float mOverallRelativePlaySpeed;
+ // How long this stream has played, in seconds.
+ time mStreamTime;
+ // Position of this stream, in seconds.
+ time mStreamPosition;
+ // Fader for the audio panning
+ Fader mPanFader;
+ // Fader for the audio volume
+ Fader mVolumeFader;
+ // Fader for the relative play speed
+ Fader mRelativePlaySpeedFader;
+ // Fader used to schedule pausing of the stream
+ Fader mPauseScheduler;
+ // Fader used to schedule stopping of the stream
+ Fader mStopScheduler;
+ // Affected by some fader
+ int mActiveFader;
+ // Current channel volumes, used to ramp the volume changes to avoid clicks
+ float mCurrentChannelVolume[MAX_CHANNELS];
+ // ID of the sound source that generated this instance
+ unsigned int mAudioSourceID;
+ // Handle of the bus this audio instance is playing on. 0 for root.
+ unsigned int mBusHandle;
+ // Filter pointer
+ FilterInstance *mFilter[FILTERS_PER_STREAM];
+ // Initialize instance. Mostly internal use.
+ void init(AudioSource &aSource, int aPlayIndex);
+ // Buffers for the resampler
+ AlignedFloatBuffer *mResampleData[2];
+ // Sub-sample playhead; 16.16 fixed point
+ unsigned int mSrcOffset;
+ // Samples left over from earlier pass
+ unsigned int mLeftoverSamples;
+ // Number of samples to delay streaming
+ unsigned int mDelaySamples;
+ // When looping, start playing from this time
+ time mLoopPoint;
+
+ // Get N samples from the stream to the buffer. Report samples written.
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize) = 0;
+ // Has the stream ended?
+ virtual bool hasEnded() = 0;
+ // Seek to certain place in the stream. Base implementation is generic "tape" seek (and slow).
+ virtual result seek(time aSeconds, float *mScratch, unsigned int mScratchSize);
+ // Rewind stream. Base implementation returns NOT_IMPLEMENTED, meaning it can't rewind.
+ virtual result rewind();
+ // Get information. Returns 0 by default.
+ virtual float getInfo(unsigned int aInfoKey);
+ };
+
+ class Soloud;
+
+ // Base class for audio sources
+ class AudioSource
+ {
+ public:
+ enum FLAGS
+ {
+ // The instances from this audio source should loop
+ SHOULD_LOOP = 1,
+ // Only one instance of this audio source should play at the same time
+ SINGLE_INSTANCE = 2,
+ // Visualization data gathering enabled. Only for busses.
+ VISUALIZATION_DATA = 4,
+ // Audio instances created from this source are affected by 3d processing
+ PROCESS_3D = 8,
+ // Audio instances created from this source have listener-relative 3d coordinates
+ LISTENER_RELATIVE = 16,
+ // Delay start of sound by the distance from listener
+ DISTANCE_DELAY = 32,
+ // If inaudible, should be killed (default)
+ INAUDIBLE_KILL = 64,
+ // If inaudible, should still be ticked (default = pause)
+ INAUDIBLE_TICK = 128
+ };
+ enum ATTENUATION_MODELS
+ {
+ // No attenuation
+ NO_ATTENUATION = 0,
+ // Inverse distance attenuation model
+ INVERSE_DISTANCE = 1,
+ // Linear distance attenuation model
+ LINEAR_DISTANCE = 2,
+ // Exponential distance attenuation model
+ EXPONENTIAL_DISTANCE = 3
+ };
+
+ // Flags. See AudioSource::FLAGS
+ unsigned int mFlags;
+ // Base sample rate, used to initialize instances
+ float mBaseSamplerate;
+ // Default volume for created instances
+ float mVolume;
+ // Number of channels this audio source produces
+ unsigned int mChannels;
+ // Sound source ID. Assigned by SoLoud the first time it's played.
+ unsigned int mAudioSourceID;
+ // 3d min distance
+ float m3dMinDistance;
+ // 3d max distance
+ float m3dMaxDistance;
+ // 3d attenuation rolloff factor
+ float m3dAttenuationRolloff;
+ // 3d attenuation model
+ unsigned int m3dAttenuationModel;
+ // 3d doppler factor
+ float m3dDopplerFactor;
+ // Filter pointer
+ Filter *mFilter[FILTERS_PER_STREAM];
+ // Pointer to the Soloud object. Needed to stop all instances in dtor.
+ Soloud *mSoloud;
+ // Pointer to a custom audio collider object
+ AudioCollider *mCollider;
+ // Pointer to custom attenuator object
+ AudioAttenuator *mAttenuator;
+ // User data related to audio collider
+ int mColliderData;
+ // When looping, start playing from this time
+ time mLoopPoint;
+
+ // CTor
+ AudioSource();
+ // Set default volume for instances
+ void setVolume(float aVolume);
+ // Set the looping of the instances created from this audio source
+ void setLooping(bool aLoop);
+ // Set whether only one instance of this sound should ever be playing at the same time
+ void setSingleInstance(bool aSingleInstance);
+
+ // Set the minimum and maximum distances for 3d audio source (closer to min distance = max vol)
+ void set3dMinMaxDistance(float aMinDistance, float aMaxDistance);
+ // Set attenuation model and rolloff factor for 3d audio source
+ void set3dAttenuation(unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+ // Set doppler factor to reduce or enhance doppler effect, default = 1.0
+ void set3dDopplerFactor(float aDopplerFactor);
+ // Set the coordinates for this audio source to be relative to listener's coordinates.
+ void set3dListenerRelative(bool aListenerRelative);
+ // Enable delaying the start of the sound based on the distance.
+ void set3dDistanceDelay(bool aDistanceDelay);
+
+ // Set a custom 3d audio collider. Set to NULL to disable.
+ void set3dCollider(AudioCollider *aCollider, int aUserData = 0);
+ // Set a custom attenuator. Set to NULL to disable.
+ void set3dAttenuator(AudioAttenuator *aAttenuator);
+
+ // Set behavior for inaudible sounds
+ void setInaudibleBehavior(bool aMustTick, bool aKill);
+
+ // Set time to jump to when looping
+ void setLoopPoint(time aLoopPoint);
+ // Get current loop point value
+ time getLoopPoint();
+
+ // Set filter. Set to NULL to clear the filter.
+ virtual void setFilter(unsigned int aFilterId, Filter *aFilter);
+ // DTor
+ virtual ~AudioSource();
+ // Create instance from the audio source. Called from within Soloud class.
+ virtual AudioSourceInstance *createInstance() = 0;
+ // Stop all instances of this audio source
+ void stop();
+ };
+};
+
+#endif
diff --git a/src/soloud/include/soloud_bassboostfilter.h b/src/soloud/include/soloud_bassboostfilter.h
new file mode 100644
index 0000000..1725184
--- /dev/null
+++ b/src/soloud/include/soloud_bassboostfilter.h
@@ -0,0 +1,63 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_BASSBOOSTFILTER_H
+#define SOLOUD_BASSBOOSTFILTER_H
+
+#include "soloud.h"
+#include "soloud_fftfilter.h"
+
+namespace SoLoud
+{
+ class BassboostFilter;
+
+ class BassboostFilterInstance : public FFTFilterInstance
+ {
+ enum FILTERATTRIBUTE
+ {
+ WET = 0,
+ BOOST = 1
+ };
+ BassboostFilter *mParent;
+ public:
+ virtual void fftFilterChannel(float *aFFTBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels);
+ BassboostFilterInstance(BassboostFilter *aParent);
+ };
+
+ class BassboostFilter : public FFTFilter
+ {
+ public:
+ enum FILTERATTRIBUTE
+ {
+ WET = 0,
+ BOOST = 1
+ };
+ float mBoost;
+ result setParams(float aBoost);
+ virtual FilterInstance *createInstance();
+ BassboostFilter();
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_biquadresonantfilter.h b/src/soloud/include/soloud_biquadresonantfilter.h
new file mode 100644
index 0000000..e83e0cf
--- /dev/null
+++ b/src/soloud/include/soloud_biquadresonantfilter.h
@@ -0,0 +1,91 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_BQRFILTER_H
+#define SOLOUD_BQRFILTER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class BiquadResonantFilter;
+
+ struct BQRStateData
+ {
+ float mY1, mY2, mX1, mX2;
+ };
+
+ class BiquadResonantFilterInstance : public FilterInstance
+ {
+ enum FILTERATTRIBUTE
+ {
+ WET = 0,
+ SAMPLERATE = 1,
+ FREQUENCY = 2,
+ RESONANCE = 3
+ };
+
+ int mActive;
+ BQRStateData mState[2];
+ float mA0, mA1, mA2, mB1, mB2;
+ int mDirty;
+ int mFilterType;
+
+ BiquadResonantFilter *mParent;
+ void calcBQRParams();
+ public:
+ virtual void filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels);
+ virtual ~BiquadResonantFilterInstance();
+ BiquadResonantFilterInstance(BiquadResonantFilter *aParent);
+ };
+
+ class BiquadResonantFilter : public Filter
+ {
+ public:
+ enum FILTERTYPE
+ {
+ NONE = 0,
+ LOWPASS = 1,
+ HIGHPASS = 2,
+ BANDPASS = 3
+ };
+ enum FILTERATTRIBUTE
+ {
+ WET = 0,
+ SAMPLERATE = 1,
+ FREQUENCY = 2,
+ RESONANCE = 3
+ };
+ int mFilterType;
+ float mSampleRate;
+ float mFrequency;
+ float mResonance;
+ virtual BiquadResonantFilterInstance *createInstance();
+ BiquadResonantFilter();
+ result setParams(int aType, float aSampleRate, float aFrequency, float aResonance);
+ virtual ~BiquadResonantFilter();
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_bus.h b/src/soloud/include/soloud_bus.h
new file mode 100644
index 0000000..84d411a
--- /dev/null
+++ b/src/soloud/include/soloud_bus.h
@@ -0,0 +1,91 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_BUS_H
+#define SOLOUD_BUS_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class Bus;
+
+ class BusInstance : public AudioSourceInstance
+ {
+ Bus *mParent;
+ unsigned int mScratchSize;
+ AlignedFloatBuffer mScratch;
+ public:
+ // Approximate volume for channels.
+ float mVisualizationChannelVolume[MAX_CHANNELS];
+ // Mono-mixed wave data for visualization and for visualization FFT input
+ float mVisualizationWaveData[256];
+
+ BusInstance(Bus *aParent);
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual bool hasEnded();
+ virtual ~BusInstance();
+ };
+
+ class Bus : public AudioSource
+ {
+ public:
+ Bus();
+ virtual BusInstance *createInstance();
+ // Set filter. Set to NULL to clear the filter.
+ virtual void setFilter(unsigned int aFilterId, Filter *aFilter);
+ // Play sound through the bus
+ handle play(AudioSource &aSound, float aVolume = 1.0f, float aPan = 0.0f, bool aPaused = 0);
+ // Play sound through the bus, delayed in relation to other sounds called via this function.
+ handle playClocked(time aSoundTime, AudioSource &aSound, float aVolume = 1.0f, float aPan = 0.0f);
+ // Start playing a 3d audio source through the bus
+ handle play3d(AudioSource &aSound, float aPosX, float aPosY, float aPosZ, float aVelX = 0.0f, float aVelY = 0.0f, float aVelZ = 0.0f, float aVolume = 1.0f, bool aPaused = 0);
+ // Start playing a 3d audio source through the bus, delayed in relation to other sounds called via this function.
+ handle play3dClocked(time aSoundTime, AudioSource &aSound, float aPosX, float aPosY, float aPosZ, float aVelX = 0.0f, float aVelY = 0.0f, float aVelZ = 0.0f, float aVolume = 1.0f);
+ // Set number of channels for the bus (default 2)
+ result setChannels(unsigned int aChannels);
+ // Enable or disable visualization data gathering
+ void setVisualizationEnable(bool aEnable);
+
+ // Calculate and get 256 floats of FFT data for visualization. Visualization has to be enabled before use.
+ float *calcFFT();
+
+ // Get 256 floats of wave data for visualization. Visualization has to be enabled before use.
+ float *getWave();
+
+ // Get approximate volume for output channel for visualization. Visualization has to be enabled before use.
+ float getApproximateVolume(unsigned int aChannel);
+ public:
+ BusInstance *mInstance;
+ unsigned int mChannelHandle;
+ // FFT output data
+ float mFFTData[256];
+ // Snapshot of wave data for visualization
+ float mWaveData[256];
+ // Internal: find the bus' channel
+ void findBusHandle();
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_c.h b/src/soloud/include/soloud_c.h
new file mode 100644
index 0000000..914347e
--- /dev/null
+++ b/src/soloud/include/soloud_c.h
@@ -0,0 +1,625 @@
+/* **************************************************
+ * WARNING: this is a generated file. Do not edit. *
+ * Any edits will be overwritten by the generator. *
+ ************************************************** */
+
+/*
+SoLoud audio engine
+Copyright (c) 2013-2016 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/* SoLoud C-Api Code Generator (c)2013-2018 Jari Komppa http://iki.fi/sol/ */
+
+#ifndef SOLOUD_C_H_INCLUDED
+#define SOLOUD_C_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Collected enumerations
+enum SOLOUD_ENUMS
+{
+ SOLOUD_AUTO = 0,
+ SOLOUD_SDL1 = 1,
+ SOLOUD_SDL2 = 2,
+ SOLOUD_PORTAUDIO = 3,
+ SOLOUD_WINMM = 4,
+ SOLOUD_XAUDIO2 = 5,
+ SOLOUD_WASAPI = 6,
+ SOLOUD_ALSA = 7,
+ SOLOUD_OSS = 8,
+ SOLOUD_OPENAL = 9,
+ SOLOUD_COREAUDIO = 10,
+ SOLOUD_OPENSLES = 11,
+ SOLOUD_VITA_HOMEBREW = 12,
+ SOLOUD_NULLDRIVER = 13,
+ SOLOUD_BACKEND_MAX = 14,
+ SOLOUD_CLIP_ROUNDOFF = 1,
+ SOLOUD_ENABLE_VISUALIZATION = 2,
+ SOLOUD_LEFT_HANDED_3D = 4,
+ BASSBOOSTFILTER_WET = 0,
+ BASSBOOSTFILTER_BOOST = 1,
+ BIQUADRESONANTFILTER_NONE = 0,
+ BIQUADRESONANTFILTER_LOWPASS = 1,
+ BIQUADRESONANTFILTER_HIGHPASS = 2,
+ BIQUADRESONANTFILTER_BANDPASS = 3,
+ BIQUADRESONANTFILTER_WET = 0,
+ BIQUADRESONANTFILTER_SAMPLERATE = 1,
+ BIQUADRESONANTFILTER_FREQUENCY = 2,
+ BIQUADRESONANTFILTER_RESONANCE = 3,
+ FLANGERFILTER_WET = 0,
+ FLANGERFILTER_DELAY = 1,
+ FLANGERFILTER_FREQ = 2,
+ LOFIFILTER_WET = 0,
+ LOFIFILTER_SAMPLERATE = 1,
+ LOFIFILTER_BITDEPTH = 2,
+ MONOTONE_SQUARE = 0,
+ MONOTONE_SAW = 1,
+ MONOTONE_SIN = 2,
+ MONOTONE_SAWSIN = 3,
+ ROBOTIZEFILTER_WET = 0,
+ SFXR_COIN = 0,
+ SFXR_LASER = 1,
+ SFXR_EXPLOSION = 2,
+ SFXR_POWERUP = 3,
+ SFXR_HURT = 4,
+ SFXR_JUMP = 5,
+ SFXR_BLIP = 6,
+ SPEECH_KW_SAW = 0,
+ SPEECH_KW_TRIANGLE = 1,
+ SPEECH_KW_SIN = 2,
+ SPEECH_KW_SQUARE = 3,
+ SPEECH_KW_PULSE = 4,
+ SPEECH_KW_NOISE = 5,
+ SPEECH_KW_WARBLE = 6,
+ VIC_PAL = 0,
+ VIC_NTSC = 1,
+ VIC_BASS = 0,
+ VIC_ALTO = 1,
+ VIC_SOPRANO = 2,
+ VIC_NOISE = 3,
+ VIC_MAX_REGS = 4
+};
+
+// Object handle typedefs
+typedef void * AlignedFloatBuffer;
+typedef void * TinyAlignedFloatBuffer;
+typedef void * Soloud;
+typedef void * AudioCollider;
+typedef void * AudioAttenuator;
+typedef void * AudioSource;
+typedef void * BassboostFilter;
+typedef void * BiquadResonantFilter;
+typedef void * Bus;
+typedef void * DCRemovalFilter;
+typedef void * EchoFilter;
+typedef void * Fader;
+typedef void * FFTFilter;
+typedef void * Filter;
+typedef void * FlangerFilter;
+typedef void * LofiFilter;
+typedef void * Monotone;
+typedef void * Openmpt;
+typedef void * Queue;
+typedef void * RobotizeFilter;
+typedef void * Prg;
+typedef void * Sfxr;
+typedef void * Speech;
+typedef void * TedSid;
+typedef void * Vic;
+typedef void * Vizsn;
+typedef void * Wav;
+typedef void * WaveShaperFilter;
+typedef void * WavStream;
+typedef void * File;
+
+/*
+ * Soloud
+ */
+void Soloud_destroy(Soloud * aSoloud);
+Soloud * Soloud_create();
+int Soloud_init(Soloud * aSoloud);
+int Soloud_initEx(Soloud * aSoloud, unsigned int aFlags /* = Soloud::CLIP_ROUNDOFF */, unsigned int aBackend /* = Soloud::AUTO */, unsigned int aSamplerate /* = Soloud::AUTO */, unsigned int aBufferSize /* = Soloud::AUTO */, unsigned int aChannels /* = 2 */);
+void Soloud_deinit(Soloud * aSoloud);
+unsigned int Soloud_getVersion(Soloud * aSoloud);
+const char * Soloud_getErrorString(Soloud * aSoloud, int aErrorCode);
+unsigned int Soloud_getBackendId(Soloud * aSoloud);
+const char * Soloud_getBackendString(Soloud * aSoloud);
+unsigned int Soloud_getBackendChannels(Soloud * aSoloud);
+unsigned int Soloud_getBackendSamplerate(Soloud * aSoloud);
+unsigned int Soloud_getBackendBufferSize(Soloud * aSoloud);
+int Soloud_setSpeakerPosition(Soloud * aSoloud, unsigned int aChannel, float aX, float aY, float aZ);
+int Soloud_getSpeakerPosition(Soloud * aSoloud, unsigned int aChannel, float * aX, float * aY, float * aZ);
+unsigned int Soloud_play(Soloud * aSoloud, AudioSource * aSound);
+unsigned int Soloud_playEx(Soloud * aSoloud, AudioSource * aSound, float aVolume /* = -1.0f */, float aPan /* = 0.0f */, int aPaused /* = 0 */, unsigned int aBus /* = 0 */);
+unsigned int Soloud_playClocked(Soloud * aSoloud, double aSoundTime, AudioSource * aSound);
+unsigned int Soloud_playClockedEx(Soloud * aSoloud, double aSoundTime, AudioSource * aSound, float aVolume /* = -1.0f */, float aPan /* = 0.0f */, unsigned int aBus /* = 0 */);
+unsigned int Soloud_play3d(Soloud * aSoloud, AudioSource * aSound, float aPosX, float aPosY, float aPosZ);
+unsigned int Soloud_play3dEx(Soloud * aSoloud, AudioSource * aSound, float aPosX, float aPosY, float aPosZ, float aVelX /* = 0.0f */, float aVelY /* = 0.0f */, float aVelZ /* = 0.0f */, float aVolume /* = 1.0f */, int aPaused /* = 0 */, unsigned int aBus /* = 0 */);
+unsigned int Soloud_play3dClocked(Soloud * aSoloud, double aSoundTime, AudioSource * aSound, float aPosX, float aPosY, float aPosZ);
+unsigned int Soloud_play3dClockedEx(Soloud * aSoloud, double aSoundTime, AudioSource * aSound, float aPosX, float aPosY, float aPosZ, float aVelX /* = 0.0f */, float aVelY /* = 0.0f */, float aVelZ /* = 0.0f */, float aVolume /* = 1.0f */, unsigned int aBus /* = 0 */);
+unsigned int Soloud_playBackground(Soloud * aSoloud, AudioSource * aSound);
+unsigned int Soloud_playBackgroundEx(Soloud * aSoloud, AudioSource * aSound, float aVolume /* = -1.0f */, int aPaused /* = 0 */, unsigned int aBus /* = 0 */);
+int Soloud_seek(Soloud * aSoloud, unsigned int aVoiceHandle, double aSeconds);
+void Soloud_stop(Soloud * aSoloud, unsigned int aVoiceHandle);
+void Soloud_stopAll(Soloud * aSoloud);
+void Soloud_stopAudioSource(Soloud * aSoloud, AudioSource * aSound);
+int Soloud_countAudioSource(Soloud * aSoloud, AudioSource * aSound);
+void Soloud_setFilterParameter(Soloud * aSoloud, unsigned int aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aValue);
+float Soloud_getFilterParameter(Soloud * aSoloud, unsigned int aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId);
+void Soloud_fadeFilterParameter(Soloud * aSoloud, unsigned int aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aTo, double aTime);
+void Soloud_oscillateFilterParameter(Soloud * aSoloud, unsigned int aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aFrom, float aTo, double aTime);
+double Soloud_getStreamTime(Soloud * aSoloud, unsigned int aVoiceHandle);
+double Soloud_getStreamPosition(Soloud * aSoloud, unsigned int aVoiceHandle);
+int Soloud_getPause(Soloud * aSoloud, unsigned int aVoiceHandle);
+float Soloud_getVolume(Soloud * aSoloud, unsigned int aVoiceHandle);
+float Soloud_getOverallVolume(Soloud * aSoloud, unsigned int aVoiceHandle);
+float Soloud_getPan(Soloud * aSoloud, unsigned int aVoiceHandle);
+float Soloud_getSamplerate(Soloud * aSoloud, unsigned int aVoiceHandle);
+int Soloud_getProtectVoice(Soloud * aSoloud, unsigned int aVoiceHandle);
+unsigned int Soloud_getActiveVoiceCount(Soloud * aSoloud);
+unsigned int Soloud_getVoiceCount(Soloud * aSoloud);
+int Soloud_isValidVoiceHandle(Soloud * aSoloud, unsigned int aVoiceHandle);
+float Soloud_getRelativePlaySpeed(Soloud * aSoloud, unsigned int aVoiceHandle);
+float Soloud_getPostClipScaler(Soloud * aSoloud);
+float Soloud_getGlobalVolume(Soloud * aSoloud);
+unsigned int Soloud_getMaxActiveVoiceCount(Soloud * aSoloud);
+int Soloud_getLooping(Soloud * aSoloud, unsigned int aVoiceHandle);
+double Soloud_getLoopPoint(Soloud * aSoloud, unsigned int aVoiceHandle);
+void Soloud_setLoopPoint(Soloud * aSoloud, unsigned int aVoiceHandle, double aLoopPoint);
+void Soloud_setLooping(Soloud * aSoloud, unsigned int aVoiceHandle, int aLooping);
+int Soloud_setMaxActiveVoiceCount(Soloud * aSoloud, unsigned int aVoiceCount);
+void Soloud_setInaudibleBehavior(Soloud * aSoloud, unsigned int aVoiceHandle, int aMustTick, int aKill);
+void Soloud_setGlobalVolume(Soloud * aSoloud, float aVolume);
+void Soloud_setPostClipScaler(Soloud * aSoloud, float aScaler);
+void Soloud_setPause(Soloud * aSoloud, unsigned int aVoiceHandle, int aPause);
+void Soloud_setPauseAll(Soloud * aSoloud, int aPause);
+int Soloud_setRelativePlaySpeed(Soloud * aSoloud, unsigned int aVoiceHandle, float aSpeed);
+void Soloud_setProtectVoice(Soloud * aSoloud, unsigned int aVoiceHandle, int aProtect);
+void Soloud_setSamplerate(Soloud * aSoloud, unsigned int aVoiceHandle, float aSamplerate);
+void Soloud_setPan(Soloud * aSoloud, unsigned int aVoiceHandle, float aPan);
+void Soloud_setPanAbsolute(Soloud * aSoloud, unsigned int aVoiceHandle, float aLVolume, float aRVolume);
+void Soloud_setPanAbsoluteEx(Soloud * aSoloud, unsigned int aVoiceHandle, float aLVolume, float aRVolume, float aLBVolume /* = 0 */, float aRBVolume /* = 0 */, float aCVolume /* = 0 */, float aSVolume /* = 0 */);
+void Soloud_setVolume(Soloud * aSoloud, unsigned int aVoiceHandle, float aVolume);
+void Soloud_setDelaySamples(Soloud * aSoloud, unsigned int aVoiceHandle, unsigned int aSamples);
+void Soloud_fadeVolume(Soloud * aSoloud, unsigned int aVoiceHandle, float aTo, double aTime);
+void Soloud_fadePan(Soloud * aSoloud, unsigned int aVoiceHandle, float aTo, double aTime);
+void Soloud_fadeRelativePlaySpeed(Soloud * aSoloud, unsigned int aVoiceHandle, float aTo, double aTime);
+void Soloud_fadeGlobalVolume(Soloud * aSoloud, float aTo, double aTime);
+void Soloud_schedulePause(Soloud * aSoloud, unsigned int aVoiceHandle, double aTime);
+void Soloud_scheduleStop(Soloud * aSoloud, unsigned int aVoiceHandle, double aTime);
+void Soloud_oscillateVolume(Soloud * aSoloud, unsigned int aVoiceHandle, float aFrom, float aTo, double aTime);
+void Soloud_oscillatePan(Soloud * aSoloud, unsigned int aVoiceHandle, float aFrom, float aTo, double aTime);
+void Soloud_oscillateRelativePlaySpeed(Soloud * aSoloud, unsigned int aVoiceHandle, float aFrom, float aTo, double aTime);
+void Soloud_oscillateGlobalVolume(Soloud * aSoloud, float aFrom, float aTo, double aTime);
+void Soloud_setGlobalFilter(Soloud * aSoloud, unsigned int aFilterId, Filter * aFilter);
+void Soloud_setVisualizationEnable(Soloud * aSoloud, int aEnable);
+float * Soloud_calcFFT(Soloud * aSoloud);
+float * Soloud_getWave(Soloud * aSoloud);
+float Soloud_getApproximateVolume(Soloud * aSoloud, unsigned int aChannel);
+unsigned int Soloud_getLoopCount(Soloud * aSoloud, unsigned int aVoiceHandle);
+float Soloud_getInfo(Soloud * aSoloud, unsigned int aVoiceHandle, unsigned int aInfoKey);
+unsigned int Soloud_createVoiceGroup(Soloud * aSoloud);
+int Soloud_destroyVoiceGroup(Soloud * aSoloud, unsigned int aVoiceGroupHandle);
+int Soloud_addVoiceToGroup(Soloud * aSoloud, unsigned int aVoiceGroupHandle, unsigned int aVoiceHandle);
+int Soloud_isVoiceGroup(Soloud * aSoloud, unsigned int aVoiceGroupHandle);
+int Soloud_isVoiceGroupEmpty(Soloud * aSoloud, unsigned int aVoiceGroupHandle);
+void Soloud_update3dAudio(Soloud * aSoloud);
+int Soloud_set3dSoundSpeed(Soloud * aSoloud, float aSpeed);
+float Soloud_get3dSoundSpeed(Soloud * aSoloud);
+void Soloud_set3dListenerParameters(Soloud * aSoloud, float aPosX, float aPosY, float aPosZ, float aAtX, float aAtY, float aAtZ, float aUpX, float aUpY, float aUpZ);
+void Soloud_set3dListenerParametersEx(Soloud * aSoloud, float aPosX, float aPosY, float aPosZ, float aAtX, float aAtY, float aAtZ, float aUpX, float aUpY, float aUpZ, float aVelocityX /* = 0.0f */, float aVelocityY /* = 0.0f */, float aVelocityZ /* = 0.0f */);
+void Soloud_set3dListenerPosition(Soloud * aSoloud, float aPosX, float aPosY, float aPosZ);
+void Soloud_set3dListenerAt(Soloud * aSoloud, float aAtX, float aAtY, float aAtZ);
+void Soloud_set3dListenerUp(Soloud * aSoloud, float aUpX, float aUpY, float aUpZ);
+void Soloud_set3dListenerVelocity(Soloud * aSoloud, float aVelocityX, float aVelocityY, float aVelocityZ);
+void Soloud_set3dSourceParameters(Soloud * aSoloud, unsigned int aVoiceHandle, float aPosX, float aPosY, float aPosZ);
+void Soloud_set3dSourceParametersEx(Soloud * aSoloud, unsigned int aVoiceHandle, float aPosX, float aPosY, float aPosZ, float aVelocityX /* = 0.0f */, float aVelocityY /* = 0.0f */, float aVelocityZ /* = 0.0f */);
+void Soloud_set3dSourcePosition(Soloud * aSoloud, unsigned int aVoiceHandle, float aPosX, float aPosY, float aPosZ);
+void Soloud_set3dSourceVelocity(Soloud * aSoloud, unsigned int aVoiceHandle, float aVelocityX, float aVelocityY, float aVelocityZ);
+void Soloud_set3dSourceMinMaxDistance(Soloud * aSoloud, unsigned int aVoiceHandle, float aMinDistance, float aMaxDistance);
+void Soloud_set3dSourceAttenuation(Soloud * aSoloud, unsigned int aVoiceHandle, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Soloud_set3dSourceDopplerFactor(Soloud * aSoloud, unsigned int aVoiceHandle, float aDopplerFactor);
+void Soloud_mix(Soloud * aSoloud, float * aBuffer, unsigned int aSamples);
+void Soloud_mixSigned16(Soloud * aSoloud, short * aBuffer, unsigned int aSamples);
+
+/*
+ * AudioAttenuator
+ */
+void AudioAttenuator_destroy(AudioAttenuator * aAudioAttenuator);
+float AudioAttenuator_attenuate(AudioAttenuator * aAudioAttenuator, float aDistance, float aMinDistance, float aMaxDistance, float aRolloffFactor);
+
+/*
+ * BassboostFilter
+ */
+void BassboostFilter_destroy(BassboostFilter * aBassboostFilter);
+int BassboostFilter_setParams(BassboostFilter * aBassboostFilter, float aBoost);
+BassboostFilter * BassboostFilter_create();
+
+/*
+ * BiquadResonantFilter
+ */
+void BiquadResonantFilter_destroy(BiquadResonantFilter * aBiquadResonantFilter);
+BiquadResonantFilter * BiquadResonantFilter_create();
+int BiquadResonantFilter_setParams(BiquadResonantFilter * aBiquadResonantFilter, int aType, float aSampleRate, float aFrequency, float aResonance);
+
+/*
+ * Bus
+ */
+void Bus_destroy(Bus * aBus);
+Bus * Bus_create();
+void Bus_setFilter(Bus * aBus, unsigned int aFilterId, Filter * aFilter);
+unsigned int Bus_play(Bus * aBus, AudioSource * aSound);
+unsigned int Bus_playEx(Bus * aBus, AudioSource * aSound, float aVolume /* = 1.0f */, float aPan /* = 0.0f */, int aPaused /* = 0 */);
+unsigned int Bus_playClocked(Bus * aBus, double aSoundTime, AudioSource * aSound);
+unsigned int Bus_playClockedEx(Bus * aBus, double aSoundTime, AudioSource * aSound, float aVolume /* = 1.0f */, float aPan /* = 0.0f */);
+unsigned int Bus_play3d(Bus * aBus, AudioSource * aSound, float aPosX, float aPosY, float aPosZ);
+unsigned int Bus_play3dEx(Bus * aBus, AudioSource * aSound, float aPosX, float aPosY, float aPosZ, float aVelX /* = 0.0f */, float aVelY /* = 0.0f */, float aVelZ /* = 0.0f */, float aVolume /* = 1.0f */, int aPaused /* = 0 */);
+unsigned int Bus_play3dClocked(Bus * aBus, double aSoundTime, AudioSource * aSound, float aPosX, float aPosY, float aPosZ);
+unsigned int Bus_play3dClockedEx(Bus * aBus, double aSoundTime, AudioSource * aSound, float aPosX, float aPosY, float aPosZ, float aVelX /* = 0.0f */, float aVelY /* = 0.0f */, float aVelZ /* = 0.0f */, float aVolume /* = 1.0f */);
+int Bus_setChannels(Bus * aBus, unsigned int aChannels);
+void Bus_setVisualizationEnable(Bus * aBus, int aEnable);
+float * Bus_calcFFT(Bus * aBus);
+float * Bus_getWave(Bus * aBus);
+float Bus_getApproximateVolume(Bus * aBus, unsigned int aChannel);
+void Bus_setVolume(Bus * aBus, float aVolume);
+void Bus_setLooping(Bus * aBus, int aLoop);
+void Bus_set3dMinMaxDistance(Bus * aBus, float aMinDistance, float aMaxDistance);
+void Bus_set3dAttenuation(Bus * aBus, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Bus_set3dDopplerFactor(Bus * aBus, float aDopplerFactor);
+void Bus_set3dListenerRelative(Bus * aBus, int aListenerRelative);
+void Bus_set3dDistanceDelay(Bus * aBus, int aDistanceDelay);
+void Bus_set3dCollider(Bus * aBus, AudioCollider * aCollider);
+void Bus_set3dColliderEx(Bus * aBus, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Bus_set3dAttenuator(Bus * aBus, AudioAttenuator * aAttenuator);
+void Bus_setInaudibleBehavior(Bus * aBus, int aMustTick, int aKill);
+void Bus_setLoopPoint(Bus * aBus, double aLoopPoint);
+double Bus_getLoopPoint(Bus * aBus);
+void Bus_stop(Bus * aBus);
+
+/*
+ * DCRemovalFilter
+ */
+void DCRemovalFilter_destroy(DCRemovalFilter * aDCRemovalFilter);
+DCRemovalFilter * DCRemovalFilter_create();
+int DCRemovalFilter_setParams(DCRemovalFilter * aDCRemovalFilter);
+int DCRemovalFilter_setParamsEx(DCRemovalFilter * aDCRemovalFilter, float aLength /* = 0.1f */);
+
+/*
+ * EchoFilter
+ */
+void EchoFilter_destroy(EchoFilter * aEchoFilter);
+EchoFilter * EchoFilter_create();
+int EchoFilter_setParams(EchoFilter * aEchoFilter, float aDelay);
+int EchoFilter_setParamsEx(EchoFilter * aEchoFilter, float aDelay, float aDecay /* = 0.7f */, float aFilter /* = 0.0f */);
+
+/*
+ * FFTFilter
+ */
+void FFTFilter_destroy(FFTFilter * aFFTFilter);
+FFTFilter * FFTFilter_create();
+
+/*
+ * FlangerFilter
+ */
+void FlangerFilter_destroy(FlangerFilter * aFlangerFilter);
+FlangerFilter * FlangerFilter_create();
+int FlangerFilter_setParams(FlangerFilter * aFlangerFilter, float aDelay, float aFreq);
+
+/*
+ * LofiFilter
+ */
+void LofiFilter_destroy(LofiFilter * aLofiFilter);
+LofiFilter * LofiFilter_create();
+int LofiFilter_setParams(LofiFilter * aLofiFilter, float aSampleRate, float aBitdepth);
+
+/*
+ * Monotone
+ */
+void Monotone_destroy(Monotone * aMonotone);
+Monotone * Monotone_create();
+int Monotone_setParams(Monotone * aMonotone, int aHardwareChannels);
+int Monotone_setParamsEx(Monotone * aMonotone, int aHardwareChannels, int aWaveform /* = SQUARE */);
+int Monotone_load(Monotone * aMonotone, const char * aFilename);
+int Monotone_loadMem(Monotone * aMonotone, unsigned char * aMem, unsigned int aLength);
+int Monotone_loadMemEx(Monotone * aMonotone, unsigned char * aMem, unsigned int aLength, int aCopy /* = false */, int aTakeOwnership /* = true */);
+int Monotone_loadFile(Monotone * aMonotone, File * aFile);
+void Monotone_setVolume(Monotone * aMonotone, float aVolume);
+void Monotone_setLooping(Monotone * aMonotone, int aLoop);
+void Monotone_set3dMinMaxDistance(Monotone * aMonotone, float aMinDistance, float aMaxDistance);
+void Monotone_set3dAttenuation(Monotone * aMonotone, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Monotone_set3dDopplerFactor(Monotone * aMonotone, float aDopplerFactor);
+void Monotone_set3dListenerRelative(Monotone * aMonotone, int aListenerRelative);
+void Monotone_set3dDistanceDelay(Monotone * aMonotone, int aDistanceDelay);
+void Monotone_set3dCollider(Monotone * aMonotone, AudioCollider * aCollider);
+void Monotone_set3dColliderEx(Monotone * aMonotone, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Monotone_set3dAttenuator(Monotone * aMonotone, AudioAttenuator * aAttenuator);
+void Monotone_setInaudibleBehavior(Monotone * aMonotone, int aMustTick, int aKill);
+void Monotone_setLoopPoint(Monotone * aMonotone, double aLoopPoint);
+double Monotone_getLoopPoint(Monotone * aMonotone);
+void Monotone_setFilter(Monotone * aMonotone, unsigned int aFilterId, Filter * aFilter);
+void Monotone_stop(Monotone * aMonotone);
+
+/*
+ * Openmpt
+ */
+void Openmpt_destroy(Openmpt * aOpenmpt);
+Openmpt * Openmpt_create();
+int Openmpt_load(Openmpt * aOpenmpt, const char * aFilename);
+int Openmpt_loadMem(Openmpt * aOpenmpt, unsigned char * aMem, unsigned int aLength);
+int Openmpt_loadMemEx(Openmpt * aOpenmpt, unsigned char * aMem, unsigned int aLength, int aCopy /* = false */, int aTakeOwnership /* = true */);
+int Openmpt_loadFile(Openmpt * aOpenmpt, File * aFile);
+void Openmpt_setVolume(Openmpt * aOpenmpt, float aVolume);
+void Openmpt_setLooping(Openmpt * aOpenmpt, int aLoop);
+void Openmpt_set3dMinMaxDistance(Openmpt * aOpenmpt, float aMinDistance, float aMaxDistance);
+void Openmpt_set3dAttenuation(Openmpt * aOpenmpt, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Openmpt_set3dDopplerFactor(Openmpt * aOpenmpt, float aDopplerFactor);
+void Openmpt_set3dListenerRelative(Openmpt * aOpenmpt, int aListenerRelative);
+void Openmpt_set3dDistanceDelay(Openmpt * aOpenmpt, int aDistanceDelay);
+void Openmpt_set3dCollider(Openmpt * aOpenmpt, AudioCollider * aCollider);
+void Openmpt_set3dColliderEx(Openmpt * aOpenmpt, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Openmpt_set3dAttenuator(Openmpt * aOpenmpt, AudioAttenuator * aAttenuator);
+void Openmpt_setInaudibleBehavior(Openmpt * aOpenmpt, int aMustTick, int aKill);
+void Openmpt_setLoopPoint(Openmpt * aOpenmpt, double aLoopPoint);
+double Openmpt_getLoopPoint(Openmpt * aOpenmpt);
+void Openmpt_setFilter(Openmpt * aOpenmpt, unsigned int aFilterId, Filter * aFilter);
+void Openmpt_stop(Openmpt * aOpenmpt);
+
+/*
+ * Queue
+ */
+void Queue_destroy(Queue * aQueue);
+Queue * Queue_create();
+int Queue_play(Queue * aQueue, AudioSource * aSound);
+unsigned int Queue_getQueueCount(Queue * aQueue);
+int Queue_isCurrentlyPlaying(Queue * aQueue, AudioSource * aSound);
+int Queue_setParamsFromAudioSource(Queue * aQueue, AudioSource * aSound);
+int Queue_setParams(Queue * aQueue, float aSamplerate);
+int Queue_setParamsEx(Queue * aQueue, float aSamplerate, unsigned int aChannels /* = 2 */);
+void Queue_setVolume(Queue * aQueue, float aVolume);
+void Queue_setLooping(Queue * aQueue, int aLoop);
+void Queue_set3dMinMaxDistance(Queue * aQueue, float aMinDistance, float aMaxDistance);
+void Queue_set3dAttenuation(Queue * aQueue, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Queue_set3dDopplerFactor(Queue * aQueue, float aDopplerFactor);
+void Queue_set3dListenerRelative(Queue * aQueue, int aListenerRelative);
+void Queue_set3dDistanceDelay(Queue * aQueue, int aDistanceDelay);
+void Queue_set3dCollider(Queue * aQueue, AudioCollider * aCollider);
+void Queue_set3dColliderEx(Queue * aQueue, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Queue_set3dAttenuator(Queue * aQueue, AudioAttenuator * aAttenuator);
+void Queue_setInaudibleBehavior(Queue * aQueue, int aMustTick, int aKill);
+void Queue_setLoopPoint(Queue * aQueue, double aLoopPoint);
+double Queue_getLoopPoint(Queue * aQueue);
+void Queue_setFilter(Queue * aQueue, unsigned int aFilterId, Filter * aFilter);
+void Queue_stop(Queue * aQueue);
+
+/*
+ * RobotizeFilter
+ */
+void RobotizeFilter_destroy(RobotizeFilter * aRobotizeFilter);
+RobotizeFilter * RobotizeFilter_create();
+
+/*
+ * Prg
+ */
+void Prg_destroy(Prg * aPrg);
+Prg * Prg_create();
+unsigned int Prg_rand(Prg * aPrg);
+void Prg_srand(Prg * aPrg, int aSeed);
+
+/*
+ * Sfxr
+ */
+void Sfxr_destroy(Sfxr * aSfxr);
+Sfxr * Sfxr_create();
+void Sfxr_resetParams(Sfxr * aSfxr);
+int Sfxr_loadParams(Sfxr * aSfxr, const char * aFilename);
+int Sfxr_loadParamsMem(Sfxr * aSfxr, unsigned char * aMem, unsigned int aLength);
+int Sfxr_loadParamsMemEx(Sfxr * aSfxr, unsigned char * aMem, unsigned int aLength, int aCopy /* = false */, int aTakeOwnership /* = true */);
+int Sfxr_loadParamsFile(Sfxr * aSfxr, File * aFile);
+int Sfxr_loadPreset(Sfxr * aSfxr, int aPresetNo, int aRandSeed);
+void Sfxr_setVolume(Sfxr * aSfxr, float aVolume);
+void Sfxr_setLooping(Sfxr * aSfxr, int aLoop);
+void Sfxr_set3dMinMaxDistance(Sfxr * aSfxr, float aMinDistance, float aMaxDistance);
+void Sfxr_set3dAttenuation(Sfxr * aSfxr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Sfxr_set3dDopplerFactor(Sfxr * aSfxr, float aDopplerFactor);
+void Sfxr_set3dListenerRelative(Sfxr * aSfxr, int aListenerRelative);
+void Sfxr_set3dDistanceDelay(Sfxr * aSfxr, int aDistanceDelay);
+void Sfxr_set3dCollider(Sfxr * aSfxr, AudioCollider * aCollider);
+void Sfxr_set3dColliderEx(Sfxr * aSfxr, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Sfxr_set3dAttenuator(Sfxr * aSfxr, AudioAttenuator * aAttenuator);
+void Sfxr_setInaudibleBehavior(Sfxr * aSfxr, int aMustTick, int aKill);
+void Sfxr_setLoopPoint(Sfxr * aSfxr, double aLoopPoint);
+double Sfxr_getLoopPoint(Sfxr * aSfxr);
+void Sfxr_setFilter(Sfxr * aSfxr, unsigned int aFilterId, Filter * aFilter);
+void Sfxr_stop(Sfxr * aSfxr);
+
+/*
+ * Speech
+ */
+void Speech_destroy(Speech * aSpeech);
+Speech * Speech_create();
+int Speech_setText(Speech * aSpeech, const char * aText);
+int Speech_setParams(Speech * aSpeech);
+int Speech_setParamsEx(Speech * aSpeech, unsigned int aBaseFrequency /* = 1330 */, float aBaseSpeed /* = 10.0f */, float aBaseDeclination /* = 0.5f */, int aBaseWaveform /* = KW_TRIANGLE */);
+void Speech_setVolume(Speech * aSpeech, float aVolume);
+void Speech_setLooping(Speech * aSpeech, int aLoop);
+void Speech_set3dMinMaxDistance(Speech * aSpeech, float aMinDistance, float aMaxDistance);
+void Speech_set3dAttenuation(Speech * aSpeech, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Speech_set3dDopplerFactor(Speech * aSpeech, float aDopplerFactor);
+void Speech_set3dListenerRelative(Speech * aSpeech, int aListenerRelative);
+void Speech_set3dDistanceDelay(Speech * aSpeech, int aDistanceDelay);
+void Speech_set3dCollider(Speech * aSpeech, AudioCollider * aCollider);
+void Speech_set3dColliderEx(Speech * aSpeech, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Speech_set3dAttenuator(Speech * aSpeech, AudioAttenuator * aAttenuator);
+void Speech_setInaudibleBehavior(Speech * aSpeech, int aMustTick, int aKill);
+void Speech_setLoopPoint(Speech * aSpeech, double aLoopPoint);
+double Speech_getLoopPoint(Speech * aSpeech);
+void Speech_setFilter(Speech * aSpeech, unsigned int aFilterId, Filter * aFilter);
+void Speech_stop(Speech * aSpeech);
+
+/*
+ * TedSid
+ */
+void TedSid_destroy(TedSid * aTedSid);
+TedSid * TedSid_create();
+int TedSid_load(TedSid * aTedSid, const char * aFilename);
+int TedSid_loadToMem(TedSid * aTedSid, const char * aFilename);
+int TedSid_loadMem(TedSid * aTedSid, unsigned char * aMem, unsigned int aLength);
+int TedSid_loadMemEx(TedSid * aTedSid, unsigned char * aMem, unsigned int aLength, int aCopy /* = false */, int aTakeOwnership /* = true */);
+int TedSid_loadFileToMem(TedSid * aTedSid, File * aFile);
+int TedSid_loadFile(TedSid * aTedSid, File * aFile);
+void TedSid_setVolume(TedSid * aTedSid, float aVolume);
+void TedSid_setLooping(TedSid * aTedSid, int aLoop);
+void TedSid_set3dMinMaxDistance(TedSid * aTedSid, float aMinDistance, float aMaxDistance);
+void TedSid_set3dAttenuation(TedSid * aTedSid, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void TedSid_set3dDopplerFactor(TedSid * aTedSid, float aDopplerFactor);
+void TedSid_set3dListenerRelative(TedSid * aTedSid, int aListenerRelative);
+void TedSid_set3dDistanceDelay(TedSid * aTedSid, int aDistanceDelay);
+void TedSid_set3dCollider(TedSid * aTedSid, AudioCollider * aCollider);
+void TedSid_set3dColliderEx(TedSid * aTedSid, AudioCollider * aCollider, int aUserData /* = 0 */);
+void TedSid_set3dAttenuator(TedSid * aTedSid, AudioAttenuator * aAttenuator);
+void TedSid_setInaudibleBehavior(TedSid * aTedSid, int aMustTick, int aKill);
+void TedSid_setLoopPoint(TedSid * aTedSid, double aLoopPoint);
+double TedSid_getLoopPoint(TedSid * aTedSid);
+void TedSid_setFilter(TedSid * aTedSid, unsigned int aFilterId, Filter * aFilter);
+void TedSid_stop(TedSid * aTedSid);
+
+/*
+ * Vic
+ */
+void Vic_destroy(Vic * aVic);
+Vic * Vic_create();
+void Vic_setModel(Vic * aVic, int model);
+int Vic_getModel(Vic * aVic);
+void Vic_setRegister(Vic * aVic, int reg, unsigned char value);
+unsigned char Vic_getRegister(Vic * aVic, int reg);
+void Vic_setVolume(Vic * aVic, float aVolume);
+void Vic_setLooping(Vic * aVic, int aLoop);
+void Vic_set3dMinMaxDistance(Vic * aVic, float aMinDistance, float aMaxDistance);
+void Vic_set3dAttenuation(Vic * aVic, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Vic_set3dDopplerFactor(Vic * aVic, float aDopplerFactor);
+void Vic_set3dListenerRelative(Vic * aVic, int aListenerRelative);
+void Vic_set3dDistanceDelay(Vic * aVic, int aDistanceDelay);
+void Vic_set3dCollider(Vic * aVic, AudioCollider * aCollider);
+void Vic_set3dColliderEx(Vic * aVic, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Vic_set3dAttenuator(Vic * aVic, AudioAttenuator * aAttenuator);
+void Vic_setInaudibleBehavior(Vic * aVic, int aMustTick, int aKill);
+void Vic_setLoopPoint(Vic * aVic, double aLoopPoint);
+double Vic_getLoopPoint(Vic * aVic);
+void Vic_setFilter(Vic * aVic, unsigned int aFilterId, Filter * aFilter);
+void Vic_stop(Vic * aVic);
+
+/*
+ * Vizsn
+ */
+void Vizsn_destroy(Vizsn * aVizsn);
+Vizsn * Vizsn_create();
+void Vizsn_setText(Vizsn * aVizsn, char * aText);
+void Vizsn_setVolume(Vizsn * aVizsn, float aVolume);
+void Vizsn_setLooping(Vizsn * aVizsn, int aLoop);
+void Vizsn_set3dMinMaxDistance(Vizsn * aVizsn, float aMinDistance, float aMaxDistance);
+void Vizsn_set3dAttenuation(Vizsn * aVizsn, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Vizsn_set3dDopplerFactor(Vizsn * aVizsn, float aDopplerFactor);
+void Vizsn_set3dListenerRelative(Vizsn * aVizsn, int aListenerRelative);
+void Vizsn_set3dDistanceDelay(Vizsn * aVizsn, int aDistanceDelay);
+void Vizsn_set3dCollider(Vizsn * aVizsn, AudioCollider * aCollider);
+void Vizsn_set3dColliderEx(Vizsn * aVizsn, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Vizsn_set3dAttenuator(Vizsn * aVizsn, AudioAttenuator * aAttenuator);
+void Vizsn_setInaudibleBehavior(Vizsn * aVizsn, int aMustTick, int aKill);
+void Vizsn_setLoopPoint(Vizsn * aVizsn, double aLoopPoint);
+double Vizsn_getLoopPoint(Vizsn * aVizsn);
+void Vizsn_setFilter(Vizsn * aVizsn, unsigned int aFilterId, Filter * aFilter);
+void Vizsn_stop(Vizsn * aVizsn);
+
+/*
+ * Wav
+ */
+void Wav_destroy(Wav * aWav);
+Wav * Wav_create();
+int Wav_load(Wav * aWav, const char * aFilename);
+int Wav_loadMem(Wav * aWav, unsigned char * aMem, unsigned int aLength);
+int Wav_loadMemEx(Wav * aWav, unsigned char * aMem, unsigned int aLength, int aCopy /* = false */, int aTakeOwnership /* = true */);
+int Wav_loadFile(Wav * aWav, File * aFile);
+int Wav_loadRawWave8(Wav * aWav, unsigned char * aMem, unsigned int aLength);
+int Wav_loadRawWave8Ex(Wav * aWav, unsigned char * aMem, unsigned int aLength, float aSamplerate /* = 44100.0f */, unsigned int aChannels /* = 1 */);
+int Wav_loadRawWave16(Wav * aWav, short * aMem, unsigned int aLength);
+int Wav_loadRawWave16Ex(Wav * aWav, short * aMem, unsigned int aLength, float aSamplerate /* = 44100.0f */, unsigned int aChannels /* = 1 */);
+int Wav_loadRawWave(Wav * aWav, float * aMem, unsigned int aLength);
+int Wav_loadRawWaveEx(Wav * aWav, float * aMem, unsigned int aLength, float aSamplerate /* = 44100.0f */, unsigned int aChannels /* = 1 */, int aCopy /* = false */, int aTakeOwnership /* = true */);
+double Wav_getLength(Wav * aWav);
+void Wav_setVolume(Wav * aWav, float aVolume);
+void Wav_setLooping(Wav * aWav, int aLoop);
+void Wav_set3dMinMaxDistance(Wav * aWav, float aMinDistance, float aMaxDistance);
+void Wav_set3dAttenuation(Wav * aWav, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void Wav_set3dDopplerFactor(Wav * aWav, float aDopplerFactor);
+void Wav_set3dListenerRelative(Wav * aWav, int aListenerRelative);
+void Wav_set3dDistanceDelay(Wav * aWav, int aDistanceDelay);
+void Wav_set3dCollider(Wav * aWav, AudioCollider * aCollider);
+void Wav_set3dColliderEx(Wav * aWav, AudioCollider * aCollider, int aUserData /* = 0 */);
+void Wav_set3dAttenuator(Wav * aWav, AudioAttenuator * aAttenuator);
+void Wav_setInaudibleBehavior(Wav * aWav, int aMustTick, int aKill);
+void Wav_setLoopPoint(Wav * aWav, double aLoopPoint);
+double Wav_getLoopPoint(Wav * aWav);
+void Wav_setFilter(Wav * aWav, unsigned int aFilterId, Filter * aFilter);
+void Wav_stop(Wav * aWav);
+
+/*
+ * WaveShaperFilter
+ */
+void WaveShaperFilter_destroy(WaveShaperFilter * aWaveShaperFilter);
+int WaveShaperFilter_setParams(WaveShaperFilter * aWaveShaperFilter, float aAmount);
+int WaveShaperFilter_setParamsEx(WaveShaperFilter * aWaveShaperFilter, float aAmount, float aWet /* = 1.0f */);
+WaveShaperFilter * WaveShaperFilter_create();
+
+/*
+ * WavStream
+ */
+void WavStream_destroy(WavStream * aWavStream);
+WavStream * WavStream_create();
+int WavStream_load(WavStream * aWavStream, const char * aFilename);
+int WavStream_loadMem(WavStream * aWavStream, unsigned char * aData, unsigned int aDataLen);
+int WavStream_loadMemEx(WavStream * aWavStream, unsigned char * aData, unsigned int aDataLen, int aCopy /* = false */, int aTakeOwnership /* = true */);
+int WavStream_loadToMem(WavStream * aWavStream, const char * aFilename);
+int WavStream_loadFile(WavStream * aWavStream, File * aFile);
+int WavStream_loadFileToMem(WavStream * aWavStream, File * aFile);
+double WavStream_getLength(WavStream * aWavStream);
+void WavStream_setVolume(WavStream * aWavStream, float aVolume);
+void WavStream_setLooping(WavStream * aWavStream, int aLoop);
+void WavStream_set3dMinMaxDistance(WavStream * aWavStream, float aMinDistance, float aMaxDistance);
+void WavStream_set3dAttenuation(WavStream * aWavStream, unsigned int aAttenuationModel, float aAttenuationRolloffFactor);
+void WavStream_set3dDopplerFactor(WavStream * aWavStream, float aDopplerFactor);
+void WavStream_set3dListenerRelative(WavStream * aWavStream, int aListenerRelative);
+void WavStream_set3dDistanceDelay(WavStream * aWavStream, int aDistanceDelay);
+void WavStream_set3dCollider(WavStream * aWavStream, AudioCollider * aCollider);
+void WavStream_set3dColliderEx(WavStream * aWavStream, AudioCollider * aCollider, int aUserData /* = 0 */);
+void WavStream_set3dAttenuator(WavStream * aWavStream, AudioAttenuator * aAttenuator);
+void WavStream_setInaudibleBehavior(WavStream * aWavStream, int aMustTick, int aKill);
+void WavStream_setLoopPoint(WavStream * aWavStream, double aLoopPoint);
+double WavStream_getLoopPoint(WavStream * aWavStream);
+void WavStream_setFilter(WavStream * aWavStream, unsigned int aFilterId, Filter * aFilter);
+void WavStream_stop(WavStream * aWavStream);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // SOLOUD_C_H_INCLUDED
+
diff --git a/src/soloud/include/soloud_dcremovalfilter.h b/src/soloud/include/soloud_dcremovalfilter.h
new file mode 100644
index 0000000..293fb38
--- /dev/null
+++ b/src/soloud/include/soloud_dcremovalfilter.h
@@ -0,0 +1,58 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_DCREMOVAL_H
+#define SOLOUD_DCREMOVAL_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class DCRemovalFilter;
+
+ class DCRemovalFilterInstance : public FilterInstance
+ {
+ float *mBuffer;
+ float *mTotals;
+ int mBufferLength;
+ DCRemovalFilter *mParent;
+ int mOffset;
+
+ public:
+ virtual void filter(float *aBuffer, unsigned int aSamples, unsigned int aChannels, float aSamplerate, time aTime);
+ virtual ~DCRemovalFilterInstance();
+ DCRemovalFilterInstance(DCRemovalFilter *aParent);
+ };
+
+ class DCRemovalFilter : public Filter
+ {
+ public:
+ float mLength;
+ virtual FilterInstance *createInstance();
+ DCRemovalFilter();
+ result setParams(float aLength = 0.1f);
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_echofilter.h b/src/soloud/include/soloud_echofilter.h
new file mode 100644
index 0000000..fcc4f8b
--- /dev/null
+++ b/src/soloud/include/soloud_echofilter.h
@@ -0,0 +1,59 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_ECHOFILTER_H
+#define SOLOUD_ECHOFILTER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class EchoFilter;
+
+ class EchoFilterInstance : public FilterInstance
+ {
+ float *mBuffer;
+ int mBufferLength;
+ EchoFilter *mParent;
+ int mOffset;
+
+ public:
+ virtual void filter(float *aBuffer, unsigned int aSamples, unsigned int aChannels, float aSamplerate, time aTime);
+ virtual ~EchoFilterInstance();
+ EchoFilterInstance(EchoFilter *aParent);
+ };
+
+ class EchoFilter : public Filter
+ {
+ public:
+ float mDelay;
+ float mDecay;
+ float mFilter;
+ virtual FilterInstance *createInstance();
+ EchoFilter();
+ result setParams(float aDelay, float aDecay = 0.7f, float aFilter = 0.0f);
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_error.h b/src/soloud/include/soloud_error.h
new file mode 100644
index 0000000..1379f8e
--- /dev/null
+++ b/src/soloud/include/soloud_error.h
@@ -0,0 +1,41 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#ifndef SOLOUD_ERROR_H
+#define SOLOUD_ERROR_H
+
+namespace SoLoud
+{
+ enum SOLOUD_ERRORS
+ {
+ SO_NO_ERROR = 0, // No error
+ INVALID_PARAMETER = 1, // Some parameter is invalid
+ FILE_NOT_FOUND = 2, // File not found
+ FILE_LOAD_FAILED = 3, // File found, but could not be loaded
+ DLL_NOT_FOUND = 4, // DLL not found, or wrong DLL
+ OUT_OF_MEMORY = 5, // Out of memory
+ NOT_IMPLEMENTED = 6, // Feature not implemented
+ UNKNOWN_ERROR = 7 // Other error
+ };
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_fader.h b/src/soloud/include/soloud_fader.h
new file mode 100644
index 0000000..bd1c93e
--- /dev/null
+++ b/src/soloud/include/soloud_fader.h
@@ -0,0 +1,63 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_FADER_H
+#define SOLOUD_FADER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ // Helper class to process faders
+ class Fader
+ {
+ public:
+ // Value to fade from
+ float mFrom;
+ // Value to fade to
+ float mTo;
+ // Delta between from and to
+ float mDelta;
+ // Total time to fade
+ time mTime;
+ // Time fading started
+ time mStartTime;
+ // Time fading will end
+ time mEndTime;
+ // Current value. Used in case time rolls over.
+ float mCurrent;
+ // Active flag; 0 means disabled, 1 is active, 2 is LFO, -1 means was active, but stopped
+ int mActive;
+ // Ctor
+ Fader();
+ // Set up LFO
+ void setLFO(float aFrom, float aTo, time aTime, time aStartTime);
+ // Set up fader
+ void set(float aFrom, float aTo, time aTime, time aStartTime);
+ // Get the current fading value
+ float get(time aCurrentTime);
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_fft.h b/src/soloud/include/soloud_fft.h
new file mode 100644
index 0000000..dd54488
--- /dev/null
+++ b/src/soloud/include/soloud_fft.h
@@ -0,0 +1,51 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_FFT_H
+#define SOLOUD_FFT_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ namespace FFT
+ {
+ // Perform 1024 unit FFT. Buffer must have 1024 floats, and will be overwritten
+ void fft1024(float *aBuffer);
+
+ // Perform 256 unit FFT. Buffer must have 256 floats, and will be overwritten
+ void fft256(float *aBuffer);
+
+ // Perform 256 unit IFFT. Buffer must have 256 floats, and will be overwritten
+ void ifft256(float *aBuffer);
+
+ // Generic (slower) power of two FFT. Buffer is overwritten.
+ void fft(float *aBuffer, unsigned int aBufferLength);
+
+ // Generic (slower) power of two IFFT. Buffer is overwritten.
+ void ifft(float *aBuffer, unsigned int aBufferLength);
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_fftfilter.h b/src/soloud/include/soloud_fftfilter.h
new file mode 100644
index 0000000..751587b
--- /dev/null
+++ b/src/soloud/include/soloud_fftfilter.h
@@ -0,0 +1,57 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_FFTFILTER_H
+#define SOLOUD_FFTFILTER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class FFTFilter;
+
+ class FFTFilterInstance : public FilterInstance
+ {
+ float *mTemp;
+ float *mInputBuffer;
+ float *mMixBuffer;
+ unsigned int mOffset[MAX_CHANNELS];
+ FFTFilter *mParent;
+ public:
+ virtual void fftFilterChannel(float *aFFTBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels);
+ virtual void filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels);
+ virtual ~FFTFilterInstance();
+ FFTFilterInstance(FFTFilter *aParent);
+ FFTFilterInstance();
+ };
+
+ class FFTFilter : public Filter
+ {
+ public:
+ virtual FilterInstance *createInstance();
+ FFTFilter();
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_file.h b/src/soloud/include/soloud_file.h
new file mode 100644
index 0000000..7667ada
--- /dev/null
+++ b/src/soloud/include/soloud_file.h
@@ -0,0 +1,90 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_FILE_H
+#define SOLOUD_FILE_H
+
+#include
+#include "soloud.h"
+
+typedef void* Soloud_Filehack;
+
+namespace SoLoud
+{
+ class File
+ {
+ public:
+ virtual ~File() {}
+ unsigned int read8();
+ unsigned int read16();
+ unsigned int read32();
+ virtual int eof() = 0;
+ virtual unsigned int read(unsigned char *aDst, unsigned int aBytes) = 0;
+ virtual unsigned int length() = 0;
+ virtual void seek(int aOffset) = 0;
+ virtual unsigned int pos() = 0;
+ virtual FILE * getFilePtr() { return 0; }
+ virtual unsigned char * getMemPtr() { return 0; }
+ };
+
+ class DiskFile : public File
+ {
+ public:
+ FILE *mFileHandle;
+
+ virtual int eof();
+ virtual unsigned int read(unsigned char *aDst, unsigned int aBytes);
+ virtual unsigned int length();
+ virtual void seek(int aOffset);
+ virtual unsigned int pos();
+ virtual ~DiskFile();
+ DiskFile();
+ DiskFile(FILE *fp);
+ result open(const char *aFilename);
+ virtual FILE * getFilePtr();
+ };
+
+ class MemoryFile : public File
+ {
+ public:
+ unsigned char *mDataPtr;
+ unsigned int mDataLength;
+ unsigned int mOffset;
+ bool mDataOwned;
+
+ virtual int eof();
+ virtual unsigned int read(unsigned char *aDst, unsigned int aBytes);
+ virtual unsigned int length();
+ virtual void seek(int aOffset);
+ virtual unsigned int pos();
+ virtual unsigned char * getMemPtr();
+ virtual ~MemoryFile();
+ MemoryFile();
+ result openMem(unsigned char *aData, unsigned int aDataLength, bool aCopy=false, bool aTakeOwnership=true);
+ result openToMem(const char *aFilename);
+ result openFileToMem(File *aFile);
+ };
+};
+
+#endif
diff --git a/src/soloud/include/soloud_file_hack_off.h b/src/soloud/include/soloud_file_hack_off.h
new file mode 100644
index 0000000..57c2ebd
--- /dev/null
+++ b/src/soloud/include/soloud_file_hack_off.h
@@ -0,0 +1,35 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/*
+See soloud_file_hack_on.h
+*/
+
+#undef FILE
+#undef fgetc
+#undef fread
+#undef fseek
+#undef ftell
+#undef fclose
+#undef fopen
diff --git a/src/soloud/include/soloud_file_hack_on.h b/src/soloud/include/soloud_file_hack_on.h
new file mode 100644
index 0000000..2be8c54
--- /dev/null
+++ b/src/soloud/include/soloud_file_hack_on.h
@@ -0,0 +1,58 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/*
+This is a "hack" header to fool third party code to use our File stuff instead
+of stdio FILE* stuff.
+You can use soloud_file_hack_off.h to undef the stuff defined here.
+*/
+
+#ifndef SEEK_SET
+#error soloud_file_hack_on must be included after stdio, otherwise the #define hacks will break stdio.
+#endif
+
+typedef void* Soloud_Filehack;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int Soloud_Filehack_fgetc(Soloud_Filehack *f);
+extern int Soloud_Filehack_fread(void *dst, int s, int c, Soloud_Filehack *f);
+extern int Soloud_Filehack_fseek(Soloud_Filehack *f, int idx, int base);
+extern int Soloud_Filehack_ftell(Soloud_Filehack *f);
+extern int Soloud_Filehack_fclose(Soloud_Filehack *f);
+extern Soloud_Filehack * Soloud_Filehack_fopen(const char *aFilename, char *aMode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#define FILE Soloud_Filehack
+#define fgetc Soloud_Filehack_fgetc
+#define fread Soloud_Filehack_fread
+#define fseek Soloud_Filehack_fseek
+#define ftell Soloud_Filehack_ftell
+#define fclose Soloud_Filehack_fclose
+#define fopen Soloud_Filehack_fopen
diff --git a/src/soloud/include/soloud_filter.h b/src/soloud/include/soloud_filter.h
new file mode 100644
index 0000000..a87fc11
--- /dev/null
+++ b/src/soloud/include/soloud_filter.h
@@ -0,0 +1,64 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_FILTER_H
+#define SOLOUD_FILTER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class Fader;
+
+ class FilterInstance
+ {
+ public:
+ unsigned int mNumParams;
+ unsigned int mParamChanged;
+ float *mParam;
+ Fader *mParamFader;
+
+
+ FilterInstance();
+ virtual result initParams(int aNumParams);
+ virtual void updateParams(time aTime);
+ virtual void filter(float *aBuffer, unsigned int aSamples, unsigned int aChannels, float aSamplerate, time aTime);
+ virtual void filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels);
+ virtual float getFilterParameter(unsigned int aAttributeId);
+ virtual void setFilterParameter(unsigned int aAttributeId, float aValue);
+ virtual void fadeFilterParameter(unsigned int aAttributeId, float aTo, time aTime, time aStartTime);
+ virtual void oscillateFilterParameter(unsigned int aAttributeId, float aFrom, float aTo, time aTime, time aStartTime);
+ virtual ~FilterInstance();
+ };
+
+ class Filter
+ {
+ public:
+ Filter();
+ virtual FilterInstance *createInstance() = 0;
+ virtual ~Filter();
+ };
+};
+
+#endif
diff --git a/src/soloud/include/soloud_flangerfilter.h b/src/soloud/include/soloud_flangerfilter.h
new file mode 100644
index 0000000..e9a949d
--- /dev/null
+++ b/src/soloud/include/soloud_flangerfilter.h
@@ -0,0 +1,65 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_FLANGERFILTER_H
+#define SOLOUD_FLANGERFILTER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class FlangerFilter;
+
+ class FlangerFilterInstance : public FilterInstance
+ {
+ float *mBuffer;
+ unsigned int mBufferLength;
+ FlangerFilter *mParent;
+ unsigned int mOffset;
+ double mIndex;
+
+ public:
+ virtual void filter(float *aBuffer, unsigned int aSamples, unsigned int aChannels, float aSamplerate, time aTime);
+ virtual ~FlangerFilterInstance();
+ FlangerFilterInstance(FlangerFilter *aParent);
+ };
+
+ class FlangerFilter : public Filter
+ {
+ public:
+ enum FILTERPARAMS
+ {
+ WET,
+ DELAY,
+ FREQ
+ };
+ float mDelay;
+ float mFreq;
+ virtual FilterInstance *createInstance();
+ FlangerFilter();
+ result setParams(float aDelay, float aFreq);
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_internal.h b/src/soloud/include/soloud_internal.h
new file mode 100644
index 0000000..7502c11
--- /dev/null
+++ b/src/soloud/include/soloud_internal.h
@@ -0,0 +1,121 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_INTERNAL_H
+#define SOLOUD_INTERNAL_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ // SDL1 back-end initialization call
+ result sdl1_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // SDL2 back-end initialization call
+ result sdl2_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // SDL1 "non-dynamic" back-end initialization call
+ result sdl1static_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // SDL2 "non-dynamic" back-end initialization call
+ result sdl2static_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // OpenAL back-end initialization call
+ result openal_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // Core Audio driver back-end initialization call
+ result coreaudio_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // OpenSL ES back-end initialization call
+ result opensles_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // PortAudio back-end initialization call
+ result portaudio_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // WinMM back-end initialization call
+ result winmm_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 4096, unsigned int aChannels = 2);
+
+ // XAudio2 back-end initialization call
+ result xaudio2_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // WASAPI back-end initialization call
+ result wasapi_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 4096, unsigned int aChannels = 2);
+
+ // OSS back-end initialization call
+ result oss_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // PS Vita homebrew back-end initialization call
+ result vita_homebrew_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // ALSA back-end initialization call
+ result alsa_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // null driver back-end initialization call
+ result null_init(SoLoud::Soloud *aSoloud, unsigned int aFlags = Soloud::CLIP_ROUNDOFF, unsigned int aSamplerate = 44100, unsigned int aBuffer = 2048, unsigned int aChannels = 2);
+
+ // Deinterlace samples in a buffer. From 12121212 to 11112222
+ void deinterlace_samples_float(const float *aSourceBuffer, float *aDestBuffer, unsigned int aSamples, unsigned int aChannels);
+
+ // Interlace samples in a buffer. From 11112222 to 12121212
+ void interlace_samples_float(const float *aSourceBuffer, float *aDestBuffer, unsigned int aSamples, unsigned int aChannels);
+
+ // Convert to 16-bit and interlace samples in a buffer. From 11112222 to 12121212
+ void interlace_samples_s16(const float *aSourceBuffer, short *aDestBuffer, unsigned int aSamples, unsigned int aChannels);
+};
+
+#define FOR_ALL_VOICES_PRE \
+ handle *h_ = NULL; \
+ handle th_[2] = { aVoiceHandle, 0 }; \
+ lockAudioMutex(); \
+ h_ = voiceGroupHandleToArray(aVoiceHandle); \
+ if (h_ == NULL) h_ = th_; \
+ while (*h_) \
+ { \
+ int ch = getVoiceFromHandle(*h_); \
+ if (ch != -1) \
+ {
+
+#define FOR_ALL_VOICES_POST \
+ } \
+ h_++; \
+ } \
+ unlockAudioMutex();
+
+#define FOR_ALL_VOICES_PRE_3D \
+ handle *h_ = NULL; \
+ handle th_[2] = { aVoiceHandle, 0 }; \
+ h_ = voiceGroupHandleToArray(aVoiceHandle); \
+ if (h_ == NULL) h_ = th_; \
+ while (*h_) \
+ { \
+ int ch = (*h_ & 0xfff) - 1; \
+ if (ch != -1 && m3dData[ch].mHandle == *h_) \
+ {
+
+#define FOR_ALL_VOICES_POST_3D \
+ } \
+ h_++; \
+ }
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_lofifilter.h b/src/soloud/include/soloud_lofifilter.h
new file mode 100644
index 0000000..99a6a3f
--- /dev/null
+++ b/src/soloud/include/soloud_lofifilter.h
@@ -0,0 +1,75 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_LOFIFILTER_H
+#define SOLOUD_LOFIFILTER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class LofiFilter;
+
+ struct LofiChannelData
+ {
+ float mSample;
+ float mSamplesToSkip;
+ };
+
+ class LofiFilterInstance : public FilterInstance
+ {
+ enum FILTERPARAMS
+ {
+ WET,
+ SAMPLERATE,
+ BITDEPTH
+ };
+ LofiChannelData mChannelData[2];
+
+ LofiFilter *mParent;
+ public:
+ virtual void filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels);
+ virtual ~LofiFilterInstance();
+ LofiFilterInstance(LofiFilter *aParent);
+ };
+
+ class LofiFilter : public Filter
+ {
+ public:
+ enum FILTERPARAMS
+ {
+ WET,
+ SAMPLERATE,
+ BITDEPTH
+ };
+ float mSampleRate;
+ float mBitdepth;
+ virtual LofiFilterInstance *createInstance();
+ LofiFilter();
+ result setParams(float aSampleRate, float aBitdepth);
+ virtual ~LofiFilter();
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_monotone.h b/src/soloud/include/soloud_monotone.h
new file mode 100644
index 0000000..bca1624
--- /dev/null
+++ b/src/soloud/include/soloud_monotone.h
@@ -0,0 +1,116 @@
+/*
+MONOTONE module for SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef MONOTONE_H
+#define MONOTONE_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class Monotone;
+ class File;
+
+ struct MonotoneSong
+ {
+ char *mTitle;
+ char *mComment;
+ unsigned char mVersion; // must be 1
+ unsigned char mTotalPatterns;
+ unsigned char mTotalTracks;
+ unsigned char mCellSize; // must be 2 for version 1
+ unsigned char mOrder[256];
+ unsigned int *mPatternData; // 64 rows * mTotalPatterns * mTotalTracks
+ };
+
+ struct MonotoneChannel
+ {
+ int mEnabled;
+ int mActive;
+ int mFreq[3];
+ int mPortamento;
+ int mArpCounter;
+ int mArp;
+ int mLastNote;
+ int mPortamentoToNote;
+ int mVibrato;
+ int mVibratoIndex;
+ int mVibratoDepth;
+ int mVibratoSpeed;
+ };
+
+ struct MonotoneHardwareChannel
+ {
+ int mEnabled;
+ float mSamplePos;
+ float mSamplePosInc;
+ };
+
+ class MonotoneInstance : public AudioSourceInstance
+ {
+ Monotone *mParent;
+ public:
+ MonotoneChannel mChannel[12];
+ MonotoneHardwareChannel mOutput[12];
+ int mNextChannel;
+ int mTempo; // ticks / row. Tick = 60hz. Default 4.
+ int mOrder;
+ int mRow;
+ int mSampleCount;
+ int mRowTick;
+
+ MonotoneInstance(Monotone *aParent);
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamples, unsigned int aBufferSize);
+ virtual bool hasEnded();
+ };
+
+ class Monotone : public AudioSource
+ {
+ public:
+ enum MONOTONE_WAVEFORMS
+ {
+ SQUARE = 0,
+ SAW = 1,
+ SIN = 2,
+ SAWSIN = 3
+ };
+
+ int mNotesHz[800];
+ int mVibTable[32];
+ int mHardwareChannels;
+ int mWaveform;
+ MonotoneSong mSong;
+ Monotone();
+ ~Monotone();
+ result setParams(int aHardwareChannels, int aWaveform = SQUARE);
+ result load(const char *aFilename);
+ result loadMem(unsigned char *aMem, unsigned int aLength, bool aCopy = false, bool aTakeOwnership = true);
+ result loadFile(File *aFile);
+ virtual AudioSourceInstance *createInstance();
+ public:
+ void clear();
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_openmpt.h b/src/soloud/include/soloud_openmpt.h
new file mode 100644
index 0000000..9a8bd54
--- /dev/null
+++ b/src/soloud/include/soloud_openmpt.h
@@ -0,0 +1,62 @@
+/*
+Openmpt module for SoLoud audio engine
+Copyright (c) 2016 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef OPENMPT_H
+#define OPENMPT_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class Openmpt;
+ class File;
+
+ class OpenmptInstance : public AudioSourceInstance
+ {
+ Openmpt *mParent;
+ void *mModfile;
+ int mPlaying;
+
+ public:
+ OpenmptInstance(Openmpt *aParent);
+ virtual ~OpenmptInstance();
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual bool hasEnded();
+ };
+
+ class Openmpt : public AudioSource
+ {
+ public:
+ char *mData;
+ unsigned int mDataLen;
+ Openmpt();
+ virtual ~Openmpt();
+ result load(const char* aFilename);
+ result loadMem(unsigned char *aMem, unsigned int aLength, bool aCopy = false, bool aTakeOwnership = true);
+ result loadFile(File *aFile);
+ virtual AudioSourceInstance *createInstance();
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_queue.h b/src/soloud/include/soloud_queue.h
new file mode 100644
index 0000000..0491f56
--- /dev/null
+++ b/src/soloud/include/soloud_queue.h
@@ -0,0 +1,72 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_QUEUE_H
+#define SOLOUD_QUEUE_H
+
+#include "soloud.h"
+
+#define SOLOUD_QUEUE_MAX 32
+
+namespace SoLoud
+{
+ class Queue;
+
+ class QueueInstance : public AudioSourceInstance
+ {
+ Queue *mParent;
+ public:
+ QueueInstance(Queue *aParent);
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual bool hasEnded();
+ virtual ~QueueInstance();
+ };
+
+ class Queue : public AudioSource
+ {
+ public:
+ Queue();
+ virtual QueueInstance *createInstance();
+ // Play sound through the queue
+ result play(AudioSource &aSound);
+ // Number of audio sources queued for replay
+ unsigned int getQueueCount();
+ // Is this audio source currently playing?
+ bool isCurrentlyPlaying(AudioSource &aSound);
+ // Set params by reading them from an audio source
+ result setParamsFromAudioSource(AudioSource &aSound);
+ // Set params manually
+ result setParams(float aSamplerate, unsigned int aChannels = 2);
+
+ public:
+ unsigned int mReadIndex, mWriteIndex, mCount;
+ AudioSourceInstance *mSource[SOLOUD_QUEUE_MAX];
+ QueueInstance *mInstance;
+ handle mQueueHandle;
+ void findQueueHandle();
+
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_robotizefilter.h b/src/soloud/include/soloud_robotizefilter.h
new file mode 100644
index 0000000..6b89cce
--- /dev/null
+++ b/src/soloud/include/soloud_robotizefilter.h
@@ -0,0 +1,60 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_ROBOTIZEFILTER_H
+#define SOLOUD_ROBOTIZEFILTER_H
+
+#include "soloud.h"
+#include "soloud_fftfilter.h"
+
+namespace SoLoud
+{
+ class RobotizeFilter;
+
+ class RobotizeFilterInstance : public FFTFilterInstance
+ {
+ enum FILTERATTRIBUTE
+ {
+ WET = 0
+ };
+ RobotizeFilter *mParent;
+ public:
+ virtual void fftFilterChannel(float *aFFTBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels);
+ RobotizeFilterInstance(RobotizeFilter *aParent);
+ };
+
+ class RobotizeFilter : public FFTFilter
+ {
+ public:
+ enum FILTERATTRIBUTE
+ {
+ WET = 0
+ };
+ float mBoost;
+ virtual FilterInstance *createInstance();
+ RobotizeFilter();
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_sfxr.h b/src/soloud/include/soloud_sfxr.h
new file mode 100644
index 0000000..169c6df
--- /dev/null
+++ b/src/soloud/include/soloud_sfxr.h
@@ -0,0 +1,169 @@
+/*
+SFXR module for SoLoud audio engine
+Copyright (c) 2014 Jari Komppa
+Based on code (c) by Tomas Pettersson, re-licensed under zlib by permission
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SFXR_H
+#define SFXR_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class File;
+
+ class Prg
+ {
+ public:
+ // random generator
+ Prg();
+ unsigned int state[16];
+ unsigned int index;
+ unsigned int rand();
+ void srand(int aSeed);
+ };
+
+ struct SfxrParams
+ {
+ int wave_type;
+
+ float p_base_freq;
+ float p_freq_limit;
+ float p_freq_ramp;
+ float p_freq_dramp;
+ float p_duty;
+ float p_duty_ramp;
+
+ float p_vib_strength;
+ float p_vib_speed;
+ float p_vib_delay;
+
+ float p_env_attack;
+ float p_env_sustain;
+ float p_env_decay;
+ float p_env_punch;
+
+ bool filter_on;
+ float p_lpf_resonance;
+ float p_lpf_freq;
+ float p_lpf_ramp;
+ float p_hpf_freq;
+ float p_hpf_ramp;
+
+ float p_pha_offset;
+ float p_pha_ramp;
+
+ float p_repeat_speed;
+
+ float p_arp_speed;
+ float p_arp_mod;
+
+ float master_vol;
+
+ float sound_vol;
+ };
+
+ class Sfxr;
+
+ class SfxrInstance : public AudioSourceInstance
+ {
+ Sfxr *mParent;
+
+ Prg mRand;
+ SfxrParams mParams;
+
+ bool playing_sample;
+ int phase;
+ double fperiod;
+ double fmaxperiod;
+ double fslide;
+ double fdslide;
+ int period;
+ float square_duty;
+ float square_slide;
+ int env_stage;
+ int env_time;
+ int env_length[3];
+ float env_vol;
+ float fphase;
+ float fdphase;
+ int iphase;
+ float phaser_buffer[1024];
+ int ipp;
+ float noise_buffer[32];
+ float fltp;
+ float fltdp;
+ float fltw;
+ float fltw_d;
+ float fltdmp;
+ float fltphp;
+ float flthp;
+ float flthp_d;
+ float vib_phase;
+ float vib_speed;
+ float vib_amp;
+ int rep_time;
+ int rep_limit;
+ int arp_time;
+ int arp_limit;
+ double arp_mod;
+
+ void resetSample(bool aRestart);
+
+ public:
+ SfxrInstance(Sfxr *aParent);
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual bool hasEnded();
+ };
+
+ class Sfxr : public AudioSource
+ {
+ public:
+ SfxrParams mParams;
+
+ enum SFXR_PRESETS
+ {
+ COIN,
+ LASER,
+ EXPLOSION,
+ POWERUP,
+ HURT,
+ JUMP,
+ BLIP
+ };
+
+ Prg mRand;
+
+ Sfxr();
+ virtual ~Sfxr();
+ void resetParams();
+ result loadParams(const char* aFilename);
+ result loadParamsMem(unsigned char *aMem, unsigned int aLength, bool aCopy = false, bool aTakeOwnership = true);
+ result loadParamsFile(File *aFile);
+
+ result loadPreset(int aPresetNo, int aRandSeed);
+ virtual AudioSourceInstance *createInstance();
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_speech.h b/src/soloud/include/soloud_speech.h
new file mode 100644
index 0000000..fa7436c
--- /dev/null
+++ b/src/soloud/include/soloud_speech.h
@@ -0,0 +1,79 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#ifndef SOLOUD_SPEECH_H
+#define SOLOUD_SPEECH_H
+
+#include "soloud.h"
+#include "../src/audiosource/speech/darray.h"
+#include "../src/audiosource/speech/klatt.h"
+#include "../src/audiosource/speech/tts.h"
+
+namespace SoLoud
+{
+ class Speech;
+
+ class Speech : public AudioSource
+ {
+ // copy of the enum in klatt.h for codegen purposes
+ enum KLATT_WAVEFORM
+ {
+ KW_SAW,
+ KW_TRIANGLE,
+ KW_SIN,
+ KW_SQUARE,
+ KW_PULSE,
+ KW_NOISE,
+ KW_WARBLE
+ };
+ public:
+ int mBaseFrequency;
+ float mBaseSpeed;
+ float mBaseDeclination;
+ int mBaseWaveform;
+ int mFrames;
+ darray mElement;
+ Speech();
+ result setText(const char *aText);
+ result setParams(unsigned int aBaseFrequency = 1330, float aBaseSpeed = 10.0f, float aBaseDeclination = 0.5f, int aBaseWaveform = KW_TRIANGLE);
+ virtual ~Speech();
+ virtual AudioSourceInstance *createInstance();
+ };
+
+ class SpeechInstance : public AudioSourceInstance
+ {
+ klatt mSynth;
+ Speech *mParent;
+ short *mSample;
+ int mSampleCount;
+ int mOffset;
+ public:
+ SpeechInstance(Speech *aParent);
+ virtual ~SpeechInstance();
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual result rewind();
+ virtual bool hasEnded();
+ };
+};
+
+#endif
diff --git a/src/soloud/include/soloud_tedsid.h b/src/soloud/include/soloud_tedsid.h
new file mode 100644
index 0000000..d238c51
--- /dev/null
+++ b/src/soloud/include/soloud_tedsid.h
@@ -0,0 +1,74 @@
+/*
+TED/SID module for SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef TEDSID_H
+#define TEDSID_H
+
+#include "soloud.h"
+
+class SIDsound;
+class TED;
+
+namespace SoLoud
+{
+ class TedSid;
+ class File;
+
+ class TedSidInstance : public AudioSourceInstance
+ {
+ TedSid *mParent;
+ SIDsound *mSID;
+ TED *mTED;
+ unsigned int mSampleCount;
+ int mNextReg;
+ int mNextVal;
+ int mRegValues[128];
+ public:
+
+ TedSidInstance(TedSid *aParent);
+ ~TedSidInstance();
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual void tick();
+ virtual bool hasEnded();
+ virtual float getInfo(unsigned int aInfoKey);
+ };
+
+ class TedSid : public AudioSource
+ {
+ public:
+ File *mFile;
+ int mModel;
+ bool mFileOwned;
+ TedSid();
+ ~TedSid();
+ result load(const char *aFilename);
+ result loadToMem(const char *aFilename);
+ result loadMem(unsigned char *aMem, unsigned int aLength, bool aCopy = false, bool aTakeOwnership = true);
+ result loadFileToMem(File *aFile);
+ result loadFile(File *aFile);
+ virtual AudioSourceInstance *createInstance();
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_thread.h b/src/soloud/include/soloud_thread.h
new file mode 100644
index 0000000..7ad7c6a
--- /dev/null
+++ b/src/soloud/include/soloud_thread.h
@@ -0,0 +1,83 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_THREAD_H
+#define SOLOUD_THREAD_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ namespace Thread
+ {
+ typedef void (*threadFunction)(void *aParam);
+
+ struct ThreadHandleData;
+ typedef ThreadHandleData* ThreadHandle;
+
+ void * createMutex();
+ void destroyMutex(void *aHandle);
+ void lockMutex(void *aHandle);
+ void unlockMutex(void *aHandle);
+
+ ThreadHandle createThread(threadFunction aThreadFunction, void *aParameter);
+
+ void sleep(int aMSec);
+ void wait(ThreadHandle aThreadHandle);
+ void release(ThreadHandle aThreadHandle);
+
+#define MAX_THREADPOOL_TASKS 1024
+
+ class PoolTask
+ {
+ public:
+ virtual void work() = 0;
+ };
+
+ class Pool
+ {
+ public:
+ // Initialize and run thread pool. For thread count 0, work is done at addWork call.
+ void init(int aThreadCount);
+ // Ctor, sets known state
+ Pool();
+ // Dtor. Waits for the threads to finish. Work may be unfinished.
+ ~Pool();
+ // Add work to work list. Object is not automatically deleted when work is done.
+ void addWork(PoolTask *aTask);
+ // Called from worker thread to get a new task. Returns null if no work available.
+ PoolTask *getWork();
+ public:
+ int mThreadCount; // number of threads
+ ThreadHandle *mThread; // array of thread handles
+ void *mWorkMutex; // mutex to protect task array/maxtask
+ PoolTask *mTaskArray[MAX_THREADPOOL_TASKS]; // pointers to tasks
+ int mMaxTask; // how many tasks are pending
+ int mRobin; // cyclic counter, used to pick jobs for threads
+ volatile int mRunning; // running flag, used to flag threads to stop
+ };
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_vic.h b/src/soloud/include/soloud_vic.h
new file mode 100644
index 0000000..ef6b486
--- /dev/null
+++ b/src/soloud/include/soloud_vic.h
@@ -0,0 +1,108 @@
+/*
+SoLoud audio engine
+Copyright (c) 2015 Jari Komppa
+
+VIC 6560/6561 sound chip emulator
+Copyright (c) 2015 Petri Hakkinen
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_VIC_H
+#define SOLOUD_VIC_H
+
+#include "soloud.h"
+
+/*
+A very bare bones emulator for Commodore VIC-20 sound chip. Supports both PAL and NTSC models.
+Bass, alto and soprano should be quite close to original vic, noise probably not so.
+
+The first three channels (bass, alto and soprano) are square waveform generators with 7-bit frequency.
+The highest bit of each oscillator register switches the oscillator on/off.
+The fourth oscillator generates a noise waveform.
+
+VIC-20 does not have per channel volume control, only global volume,
+which you can change by setting audio source's volume.
+
+To get that authentic moldy VIC-20 sound, the audio source should be coupled with a biquad resonant filter
+with the following params: type = LOWPASS, sample rate = 44100, frequency = 1500, resonance = 2.0.
+*/
+
+namespace SoLoud
+{
+ class Vic;
+
+ class VicInstance : public AudioSourceInstance
+ {
+ public:
+ VicInstance(Vic *aParent);
+ ~VicInstance();
+
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual bool hasEnded();
+
+ public:
+ Vic* m_parent;
+ unsigned int m_phase[4];
+ unsigned int m_noisePos;
+ };
+
+ class Vic : public AudioSource
+ {
+ public:
+ // VIC model
+ enum
+ {
+ PAL = 0,
+ NTSC
+ };
+
+ // VIC sound registers
+ enum
+ {
+ BASS = 0,
+ ALTO,
+ SOPRANO,
+ NOISE,
+ MAX_REGS
+ };
+
+ Vic();
+
+ virtual ~Vic();
+
+ void setModel(int model);
+
+ int getModel() const;
+
+ void setRegister(int reg, unsigned char value);
+
+ unsigned char getRegister(int reg);
+
+ public:
+ virtual AudioSourceInstance *createInstance();
+ int m_model;
+ float m_clocks[4]; // base clock frequencies for oscillators, dependent on VIC model
+ unsigned char m_regs[MAX_REGS];
+ unsigned char m_noise[8192];
+ };
+};
+
+#endif
diff --git a/src/soloud/include/soloud_vizsn.h b/src/soloud/include/soloud_vizsn.h
new file mode 100644
index 0000000..35a229d
--- /dev/null
+++ b/src/soloud/include/soloud_vizsn.h
@@ -0,0 +1,82 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+vizsn speech synthesizer (c) by Ville-Matias Heikkilä,
+released under WTFPL, http://www.wtfpl.net/txt/copying/
+(in short, "do whatever you want to")
+
+Integration and changes to work with SoLoud by Jari Komppa,
+released under same license.
+*/
+
+#ifndef SOLOUD_VIZSN_H
+#define SOLOUD_VIZSN_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class Vizsn;
+
+ struct VizsnResonator
+ {
+ public:
+ float a, b, c, p1, p2;
+
+ float resonate(float i);
+ float antiresonate(float i);
+ };
+
+ struct VizsnBank
+ {
+ VizsnResonator r[10];
+ float pitch;
+ float frica, voice, aspir, bypas, breth;
+ };
+
+ class VizsnInstance : public AudioSourceInstance
+ {
+ public:
+ VizsnInstance(Vizsn *aParent);
+ ~VizsnInstance();
+
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual bool hasEnded();
+
+ public:
+ Vizsn *mParent;
+ VizsnBank mBank0, mBank1, mBank0to1;
+ int mNper, mNmod, mNopen;
+ int mEchobuf[1024], mPtr;
+ int mCurrentVoiceType;
+ float mPitch;
+ char *mS;
+ float mBuf[2048];
+ unsigned int mBufwrite;
+ unsigned int mBufread;
+ float vcsrc(int aPitch, int aVoicetype);
+ float noisrc();
+ float genwave();
+ void setphone(VizsnBank *aB, char aP, float aPitch);
+ void slidePrepare(int aNumtix);
+ void slideTick();
+ int mA;
+ int mB;
+ int mOrgv;
+ float mGlotlast;
+ };
+
+ class Vizsn : public AudioSource
+ {
+ public:
+ char *mText;
+ Vizsn();
+ virtual ~Vizsn();
+ void setText(char *aText);
+ public:
+ virtual AudioSourceInstance *createInstance();
+ };
+};
+
+#endif
diff --git a/src/soloud/include/soloud_wav.h b/src/soloud/include/soloud_wav.h
new file mode 100644
index 0000000..887c16b
--- /dev/null
+++ b/src/soloud/include/soloud_wav.h
@@ -0,0 +1,74 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_WAV_H
+#define SOLOUD_WAV_H
+
+#include "soloud.h"
+
+struct stb_vorbis;
+
+namespace SoLoud
+{
+ class Wav;
+ class File;
+ class MemoryFile;
+
+ class WavInstance : public AudioSourceInstance
+ {
+ Wav *mParent;
+ unsigned int mOffset;
+ public:
+ WavInstance(Wav *aParent);
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual result rewind();
+ virtual bool hasEnded();
+ };
+
+ class Wav : public AudioSource
+ {
+ result loadwav(MemoryFile *aReader);
+ result loadogg(MemoryFile *aReader);
+ result loadmp3(MemoryFile *aReader);
+ result loadflac(MemoryFile *aReader);
+ result testAndLoadFile(MemoryFile *aReader);
+ public:
+ float *mData;
+ unsigned int mSampleCount;
+
+ Wav();
+ virtual ~Wav();
+ result load(const char *aFilename);
+ result loadMem(unsigned char *aMem, unsigned int aLength, bool aCopy = false, bool aTakeOwnership = true);
+ result loadFile(File *aFile);
+ result loadRawWave8(unsigned char *aMem, unsigned int aLength, float aSamplerate = 44100.0f, unsigned int aChannels = 1);
+ result loadRawWave16(short *aMem, unsigned int aLength, float aSamplerate = 44100.0f, unsigned int aChannels = 1);
+ result loadRawWave(float *aMem, unsigned int aLength, float aSamplerate = 44100.0f, unsigned int aChannels = 1, bool aCopy = false, bool aTakeOwnership = true);
+
+ virtual AudioSourceInstance *createInstance();
+ time getLength();
+ };
+};
+
+#endif
diff --git a/src/soloud/include/soloud_waveshaperfilter.h b/src/soloud/include/soloud_waveshaperfilter.h
new file mode 100644
index 0000000..11348a1
--- /dev/null
+++ b/src/soloud/include/soloud_waveshaperfilter.h
@@ -0,0 +1,54 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_WAVESHAPERFILTER_H
+#define SOLOUD_WAVESHAPERFILTER_H
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ class WaveShaperFilter;
+
+ class WaveShaperFilterInstance : public FilterInstance
+ {
+ WaveShaperFilter *mParent;
+ public:
+ virtual void filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels);
+ virtual ~WaveShaperFilterInstance();
+ WaveShaperFilterInstance(WaveShaperFilter *aParent);
+ };
+
+ class WaveShaperFilter : public Filter
+ {
+ public:
+ float mAmount, mWet;
+ virtual WaveShaperFilterInstance *createInstance();
+ result setParams(float aAmount, float aWet = 1.0f);
+ WaveShaperFilter();
+ virtual ~WaveShaperFilter();
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/include/soloud_wavstream.h b/src/soloud/include/soloud_wavstream.h
new file mode 100644
index 0000000..ba7739c
--- /dev/null
+++ b/src/soloud/include/soloud_wavstream.h
@@ -0,0 +1,106 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef SOLOUD_WAVSTREAM_H
+#define SOLOUD_WAVSTREAM_H
+
+#include
+#include "soloud.h"
+
+struct stb_vorbis;
+#ifndef dr_flac_h
+struct drflac;
+#endif
+#ifndef dr_mp3_h
+struct drmp3;
+#endif
+#ifndef dr_wav_h
+struct drwav;
+#endif
+
+namespace SoLoud
+{
+ class WavStream;
+ class File;
+
+ class WavStreamInstance : public AudioSourceInstance
+ {
+ WavStream *mParent;
+ unsigned int mOffset;
+ File *mFile;
+ union codec
+ {
+ stb_vorbis *mOgg;
+ drflac *mFlac;
+ drmp3 *mMp3;
+ drwav *mWav;
+ } mCodec;
+ unsigned int mOggFrameSize;
+ unsigned int mOggFrameOffset;
+ float **mOggOutputs;
+ public:
+ WavStreamInstance(WavStream *aParent);
+ virtual unsigned int getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize);
+ virtual result rewind();
+ virtual bool hasEnded();
+ virtual ~WavStreamInstance();
+ };
+
+ enum WAVSTREAM_FILETYPE
+ {
+ WAVSTREAM_WAV = 0,
+ WAVSTREAM_OGG = 1,
+ WAVSTREAM_FLAC = 2,
+ WAVSTREAM_MP3 = 3
+ };
+
+ class WavStream : public AudioSource
+ {
+ result loadwav(File *fp);
+ result loadogg(File *fp);
+ result loadflac(File *fp);
+ result loadmp3(File *fp);
+ public:
+ int mFiletype;
+ char *mFilename;
+ File *mMemFile;
+ File *mStreamFile;
+ unsigned int mSampleCount;
+
+ WavStream();
+ virtual ~WavStream();
+ result load(const char *aFilename);
+ result loadMem(unsigned char *aData, unsigned int aDataLen, bool aCopy = false, bool aTakeOwnership = true);
+ result loadToMem(const char *aFilename);
+ result loadFile(File *aFile);
+ result loadFileToMem(File *aFile);
+ virtual AudioSourceInstance *createInstance();
+ time getLength();
+
+ public:
+ result parse(File *aFile);
+ };
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/monotone/soloud_monotone.cpp b/src/soloud/src/audiosource/monotone/soloud_monotone.cpp
new file mode 100644
index 0000000..ee2593d
--- /dev/null
+++ b/src/soloud/src/audiosource/monotone/soloud_monotone.cpp
@@ -0,0 +1,476 @@
+/*
+MONOTONE module for SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include
+#include
+#include "soloud_monotone.h"
+#include "soloud_file.h"
+
+namespace SoLoud
+{
+
+ MonotoneInstance::MonotoneInstance(Monotone *aParent)
+ {
+ mParent = aParent;
+ mOrder = 0;
+ mRow = 0;
+ mTempo = 4;
+ mSampleCount = 0;
+ mNextChannel = 0;
+ mRowTick = 0;
+ int i;
+ for (i = 0; i < 12; i++)
+ {
+ mOutput[i].mSamplePos = 0;
+ mOutput[i].mSamplePosInc = 0;
+ mOutput[i].mEnabled = i < mParent->mHardwareChannels && i < mParent->mSong.mTotalTracks;
+ mChannel[i].mEnabled = i < mParent->mSong.mTotalTracks;
+ mChannel[i].mActive = 0;
+ mChannel[i].mArpCounter = 0;
+ mChannel[i].mLastNote = 0;
+ mChannel[i].mPortamentoToNote = 0;
+ mChannel[i].mArp = 0;
+ mChannel[i].mVibrato = 0;
+ mChannel[i].mVibratoIndex = 0;
+ mChannel[i].mVibratoDepth = 1;
+ mChannel[i].mVibratoSpeed = 1;
+ }
+ }
+
+ unsigned int MonotoneInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ int samplesPerTick = (int)floor(mSamplerate / 60);
+ unsigned int i;
+ for (i = 0; i < 12; i++)
+ {
+ mOutput[i].mEnabled = i < (unsigned int)mParent->mHardwareChannels && i < (unsigned int)mParent->mSong.mTotalTracks;
+ }
+ for (i = 0; i < aSamplesToRead; i++)
+ {
+ if ((mSampleCount % samplesPerTick) == 0)
+ {
+ // new tick
+ mRowTick++;
+ if (mRowTick >= mTempo)
+ {
+ mRowTick = 0;
+ // Process row
+ int patternjump = mOrder + 1;
+ int rowjump = 0;
+ int dojump = 0;
+ int pattern = mParent->mSong.mOrder[mOrder];
+ int j;
+ for (j = 0; j < mParent->mSong.mTotalTracks; j++)
+ {
+ unsigned int d = mParent->mSong.mPatternData[(pattern * 64 + mRow) * mParent->mSong.mTotalTracks + j];
+ unsigned int note = (d >> 9) & 127;
+ unsigned int effect = (d >> 6) & 7;
+ unsigned int effectdata = (d)& 63;
+ unsigned int effectdata1 = (d >> 3) & 7;
+ unsigned int effectdata2 = (d >> 0) & 7;
+
+ // by default, effects are off, and have to be set on every row.
+ mChannel[j].mPortamento = 0;
+ mChannel[j].mArp = 0;
+ mChannel[j].mVibrato = 0;
+
+ int oldhz = mChannel[j].mFreq[0];
+
+ if (note == 127)
+ {
+ // noteEnd
+ mChannel[j].mActive = 0;
+ mChannel[j].mFreq[0] = 0;
+ mChannel[j].mFreq[1] = 0;
+ mChannel[j].mFreq[2] = 0;
+ mChannel[j].mPortamento = 0;
+ mChannel[j].mLastNote = 0;
+ }
+ else
+ if (note != 0)
+ {
+ mChannel[j].mActive = 1;
+ mChannel[j].mFreq[0] = mParent->mNotesHz[note * 8];
+ mChannel[j].mFreq[1] = mChannel[j].mFreq[0];
+ mChannel[j].mFreq[2] = mChannel[j].mFreq[0];
+ mChannel[j].mPortamento = 0;
+ mChannel[j].mLastNote = note;
+ mChannel[j].mVibratoIndex = 0;
+ }
+ else
+ if (note == 0)
+ {
+ note = mChannel[j].mLastNote;
+ }
+
+ switch (effect)
+ {
+ case 0x0:
+ // arp
+ mChannel[j].mFreq[1] = mParent->mNotesHz[(note + effectdata1) * 8];
+ mChannel[j].mFreq[2] = mParent->mNotesHz[(note + effectdata2) * 8];
+ if (effectdata1 || effectdata2)
+ mChannel[j].mArp = 1;
+ break;
+ case 0x1:
+ // portamento up
+ mChannel[j].mPortamento = effectdata;
+ break;
+ case 0x2:
+ // portamento down
+ mChannel[j].mPortamento = -(signed)effectdata;
+ break;
+ case 0x3:
+ // portamento to note
+ mChannel[j].mPortamentoToNote = mParent->mNotesHz[note * 8];
+ if (oldhz != mChannel[j].mPortamentoToNote)
+ {
+ mChannel[j].mFreq[0] = oldhz;
+ mChannel[j].mPortamento = effectdata;
+ if (oldhz > mChannel[j].mPortamentoToNote)
+ mChannel[j].mPortamento *= -1;
+ }
+ else
+ {
+ mChannel[j].mPortamentoToNote = 0;
+ }
+ break;
+ case 0x4:
+ // vibrato
+ mChannel[j].mVibrato = 1;
+ if (effectdata2 != 0) mChannel[j].mVibratoDepth = effectdata2;
+ if (effectdata1 != 0) mChannel[j].mVibratoSpeed = effectdata1;
+ break;
+ case 0x5:
+ // pattern jump
+ patternjump = effectdata;
+ dojump = 1;
+ break;
+ case 0x6:
+ // row jump
+ rowjump = effectdata;
+ dojump = 1;
+ break;
+ case 0x7:
+ // set speed
+ mTempo = effectdata;
+ break;
+ }
+ }
+
+ mRow++;
+
+ if (dojump)
+ {
+ mRow = rowjump;
+ mOrder = patternjump;
+ }
+
+ if (mRow == 64)
+ {
+ mRow = 0;
+ mOrder++;
+ if (mParent->mSong.mOrder[mOrder] == 0xff)
+ mOrder = 0;
+ }
+ }
+
+ int j;
+
+ // per tick events
+ for (j = 0; j < mParent->mSong.mTotalTracks; j++)
+ {
+ if (mChannel[j].mActive)
+ {
+ if (mChannel[j].mVibrato)
+ {
+ mChannel[j].mFreq[0] = mParent->mNotesHz[mChannel[j].mLastNote * 8 + (mParent->mVibTable[mChannel[j].mVibratoIndex] * mChannel[j].mVibratoDepth) / 64];
+ mChannel[j].mVibratoIndex += mChannel[j].mVibratoSpeed;
+ mChannel[j].mVibratoIndex %= 32;
+ }
+ if (mChannel[j].mPortamento && mRowTick != 0)
+ {
+ mChannel[j].mFreq[0] += mChannel[j].mPortamento;
+ if (mChannel[j].mPortamentoToNote)
+ {
+ if ((mChannel[j].mPortamento > 0 && mChannel[j].mFreq[0] >= mChannel[j].mPortamentoToNote) ||
+ (mChannel[j].mPortamento < 0 && mChannel[j].mFreq[0] <= mChannel[j].mPortamentoToNote))
+ {
+ mChannel[j].mFreq[0] = mChannel[j].mPortamentoToNote;
+ mChannel[j].mPortamentoToNote = 0;
+ }
+ }
+ }
+ }
+ }
+
+ // Channel fill
+
+ int gotit = 0;
+ int tries = 0;
+
+ for (j = 0; j < mParent->mHardwareChannels; j++)
+ mOutput[j].mSamplePosInc = 0;
+
+ while (gotit < mParent->mHardwareChannels && tries < mParent->mSong.mTotalTracks)
+ {
+ if (mChannel[mNextChannel].mActive)
+ {
+ if (mChannel[mNextChannel].mArp)
+ {
+ mOutput[gotit].mSamplePosInc = 1.0f / (mSamplerate / mChannel[mNextChannel].mFreq[mChannel[mNextChannel].mArpCounter]);
+ mChannel[mNextChannel].mArpCounter++;
+ mChannel[mNextChannel].mArpCounter %= 3;
+ }
+ else
+ {
+ mOutput[gotit].mSamplePosInc = 1.0f / (mSamplerate / mChannel[mNextChannel].mFreq[0]);
+ }
+ gotit++;
+ }
+ mNextChannel++;
+ mNextChannel %= mParent->mSong.mTotalTracks;
+ tries++;
+ }
+ }
+
+ aBuffer[i] = 0;
+ int j;
+ switch (mParent->mWaveform)
+ {
+ case Monotone::SAW:
+ for (j = 0; j < 12; j++)
+ {
+ if (mOutput[j].mEnabled)
+ {
+ float bleh = mOutput[j].mSamplePos + mOutput[j].mSamplePosInc;
+ mOutput[j].mSamplePos = bleh - (long)bleh;
+ // saw:
+ aBuffer[i] += ((mOutput[j].mSamplePos) - 0.5f) * 0.5f;
+ }
+ }
+ break;
+ case Monotone::SIN:
+ for (j = 0; j < 12; j++)
+ {
+ if (mOutput[j].mEnabled)
+ {
+ float bleh = mOutput[j].mSamplePos + mOutput[j].mSamplePosInc;
+ mOutput[j].mSamplePos = bleh - (long)bleh;
+ // sin:
+ aBuffer[i] += (float)sin(mOutput[j].mSamplePos * M_PI * 2) * 0.5f;
+ }
+ }
+ break;
+ case Monotone::SAWSIN:
+ for (j = 0; j < 12; j++)
+ {
+ if (mOutput[j].mEnabled)
+ {
+ float bleh = mOutput[j].mSamplePos + mOutput[j].mSamplePosInc;
+ mOutput[j].mSamplePos = bleh - (long)bleh;
+ // sawsin:
+ bleh = ((mOutput[j].mSamplePos) - 0.5f);
+ bleh *= (float)sin(mOutput[j].mSamplePos * M_PI * 2);
+ aBuffer[i] += bleh;
+ }
+ }
+ break;
+ case Monotone::SQUARE:
+ default:
+ for (j = 0; j < 12; j++)
+ {
+ if (mOutput[j].mEnabled)
+ {
+ float bleh = mOutput[j].mSamplePos + mOutput[j].mSamplePosInc;
+ mOutput[j].mSamplePos = bleh - (long)bleh;
+ // square:
+ aBuffer[i] += (mOutput[j].mSamplePos > 0.5f) ? 0.25f : -0.25f;
+ }
+ }
+ break;
+ }
+
+ mSampleCount++;
+ }
+ return aSamplesToRead;
+ }
+
+ bool MonotoneInstance::hasEnded()
+ {
+ return 0;
+ }
+
+ Monotone::Monotone()
+ {
+ int i;
+ float temphz = 27.5f;
+ int IBO = 12; // Intervals Between Octaves
+ int IBN = 8; // Intervals Between Notes
+ float interval = 1.00724641222f;//exp(ln(2)/(IBO*IBN));
+ int maxnote = 3 + (8 * IBO) + 1;
+
+ mNotesHz[0] = 440;
+ mNotesHz[1 * IBN] = (int)floor(temphz + 0.5f);
+
+ for (i = (1 * IBN) - 1; i > 1; i--)
+ {
+ temphz = temphz / interval;
+ if (temphz < 19) temphz = 19; // orig limitation, we could go lower though
+ mNotesHz[i] = (int)floor(temphz + 0.5f);
+ }
+ temphz = 27.5f;
+ for (i = (1 * IBN) + 1; i < maxnote * IBN; i++)
+ {
+ temphz = temphz * interval;
+ mNotesHz[i] = (int)floor(temphz + 0.5f);
+ }
+
+ for (i = 0; i < 32; i++)
+ mVibTable[i] = (int)floor(0.5 + 64 * sin(i * M_PI / 32 * 2));
+
+ mSong.mTitle = 0;
+ mSong.mComment = 0;
+ mSong.mPatternData = 0;
+
+ mBaseSamplerate = 44100;
+ mChannels = 1;
+
+ mHardwareChannels = 1;
+ mWaveform = SQUARE;
+ }
+
+ void Monotone::clear()
+ {
+ stop();
+
+ delete[] mSong.mTitle;
+ delete[] mSong.mComment;
+ delete[] mSong.mPatternData;
+
+ mSong.mTitle = 0;
+ mSong.mComment = 0;
+ mSong.mPatternData = 0;
+ }
+
+ Monotone::~Monotone()
+ {
+ stop();
+ clear();
+ }
+
+ static char * mystrdup(const char *src)
+ {
+ int len = (int)strlen(src);
+ char * res = new char[len + 1];
+ memcpy(res, src, len);
+ res[len] = 0;
+ return res;
+ }
+
+ result Monotone::setParams(int aHardwareChannels, int aWaveform)
+ {
+ if (aHardwareChannels <= 0 || aWaveform < 0)
+ return INVALID_PARAMETER;
+ mHardwareChannels = aHardwareChannels;
+ mWaveform = aWaveform;
+ return SO_NO_ERROR;
+ }
+
+ result Monotone::loadMem(unsigned char *aMem, unsigned int aLength, bool aCopy, bool aTakeOwnership)
+ {
+ MemoryFile mf;
+ int res = mf.openMem(aMem, aLength, aCopy, aTakeOwnership);
+ if (res != SO_NO_ERROR)
+ return res;
+ return loadFile(&mf);
+ }
+
+ result Monotone::load(const char *aFilename)
+ {
+ DiskFile df;
+ int res = df.open(aFilename);
+ if (res != SO_NO_ERROR)
+ return res;
+ return loadFile(&df);
+ }
+
+ result Monotone::loadFile(File *aFile)
+ {
+ if (aFile == NULL)
+ return INVALID_PARAMETER;
+ clear();
+ int i;
+ unsigned char temp[200];
+ aFile->read(temp, 9);
+ char magic[] = "\bMONOTONE";
+ for (i = 0; i < 9; i++)
+ {
+ if (temp[i] != magic[i])
+ {
+ return FILE_LOAD_FAILED;
+ }
+ }
+ aFile->read(temp, 41);
+ temp[temp[0] + 1] = 0; // pascal -> asciiz: pascal strings have length as first byte
+ mSong.mTitle = mystrdup((char*)temp + 1);
+ aFile->read(temp, 41);
+ temp[temp[0] + 1] = 0; // pascal -> asciiz: pascal strings have length as first byte
+ mSong.mComment = mystrdup((char*)temp + 1);
+ aFile->read(temp, 4);
+ mSong.mVersion = temp[0];
+ mSong.mTotalPatterns = temp[1];
+ mSong.mTotalTracks = temp[2];
+ mSong.mCellSize = temp[3];
+ if (mSong.mVersion != 1 || mSong.mCellSize != 2)
+ {
+ return FILE_LOAD_FAILED;
+ }
+ aFile->read(mSong.mOrder, 256);
+ int totalnotes = 64 * mSong.mTotalPatterns * mSong.mTotalTracks;
+ mSong.mPatternData = new unsigned int[totalnotes];
+ for (i = 0; i < totalnotes; i++)
+ {
+ aFile->read(temp, 2);
+ unsigned int datavalue = temp[0] | (temp[1] << 8);
+ mSong.mPatternData[i] = datavalue;
+ //unsigned int note = (datavalue >> 9) & 127;
+ //unsigned int effect = (datavalue >> 6) & 7;
+ //unsigned int effectdata = (datavalue)& 63;
+ //unsigned int effectdata1 = (datavalue >> 3) & 7;
+ //unsigned int effectdata2 = (datavalue >> 0) & 7;
+ }
+
+ return SO_NO_ERROR;
+ }
+
+
+ AudioSourceInstance * Monotone::createInstance()
+ {
+ return new MonotoneInstance(this);
+ }
+
+};
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/openmpt/soloud_openmpt.cpp b/src/soloud/src/audiosource/openmpt/soloud_openmpt.cpp
new file mode 100644
index 0000000..6c64e51
--- /dev/null
+++ b/src/soloud/src/audiosource/openmpt/soloud_openmpt.cpp
@@ -0,0 +1,151 @@
+/*
+Openmpt module for SoLoud audio engine
+Copyright (c) 2016 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include "soloud_openmpt.h"
+#include "soloud_file.h"
+
+extern "C"
+{
+ void * openmpt_module_create_from_memory(const void * filedata, size_t filesize, void *logfunc, void * user,void * ctls);
+ void openmpt_module_destroy(void * mod);
+ int openmpt_module_read_float_stereo(void * mod, int samplerate, size_t count, float * left, float * right);
+}
+
+namespace SoLoud
+{
+ OpenmptInstance::OpenmptInstance(Openmpt *aParent)
+ {
+ mParent = aParent;
+ mModfile = openmpt_module_create_from_memory((const void*)mParent->mData, mParent->mDataLen, NULL, NULL, NULL);
+ mPlaying = mModfile != NULL;
+ }
+
+ unsigned int OpenmptInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ if (mModfile == NULL)
+ return 0;
+ int s = aSamplesToRead;
+ unsigned int outofs = 0;
+
+ while (s && mPlaying)
+ {
+ int samples = 512;
+ if (s < samples) samples = s;
+ int res = openmpt_module_read_float_stereo(mModfile, (int)floor(mSamplerate), samples, aBuffer + outofs, aBuffer + outofs + aBufferSize);
+ if (res == 0)
+ {
+ mPlaying = 0;
+ return outofs;
+ }
+ outofs += samples;
+ s -= samples;
+ }
+
+ return outofs;
+ }
+
+ bool OpenmptInstance::hasEnded()
+ {
+ return !mPlaying;
+ }
+
+ OpenmptInstance::~OpenmptInstance()
+ {
+ if (mModfile)
+ {
+ openmpt_module_destroy(mModfile);
+ }
+ mModfile = 0;
+ }
+
+ result Openmpt::loadMem(unsigned char *aMem, unsigned int aLength, bool aCopy, bool aTakeOwnership)
+ {
+ MemoryFile mf;
+ int res = mf.openMem(aMem, aLength, aCopy, aTakeOwnership);
+ if (res != SO_NO_ERROR)
+ return res;
+ return loadFile(&mf);
+ }
+
+ result Openmpt::load(const char *aFilename)
+ {
+ DiskFile df;
+ int res = df.open(aFilename);
+ if (res != SO_NO_ERROR)
+ return res;
+ return loadFile(&df);
+ }
+
+ result Openmpt::loadFile(File *aFile)
+ {
+ if (mData)
+ {
+ delete[] mData;
+ }
+
+ mDataLen = aFile->length();
+ mData = new char[mDataLen];
+ if (!mData)
+ {
+ mData = 0;
+ mDataLen = 0;
+ return OUT_OF_MEMORY;
+ }
+ aFile->read((unsigned char*)mData, mDataLen);
+
+ void *mpf = openmpt_module_create_from_memory((const void*)mData, mDataLen, NULL, NULL, NULL);
+ if (!mpf)
+ {
+ delete[] mData;
+ mDataLen = 0;
+ return FILE_LOAD_FAILED;
+ }
+ openmpt_module_destroy(mpf);
+ return 0;
+ }
+
+ Openmpt::Openmpt()
+ {
+ mBaseSamplerate = 44100;
+ mChannels = 2;
+ mData = 0;
+ mDataLen = 0;
+ }
+
+ Openmpt::~Openmpt()
+ {
+ stop();
+ delete[] mData;
+ mData = 0;
+ mDataLen = 0;
+ }
+
+ AudioSourceInstance * Openmpt::createInstance()
+ {
+ return new OpenmptInstance(this);
+ }
+
+};
diff --git a/src/soloud/src/audiosource/openmpt/soloud_openmpt_dll.c b/src/soloud/src/audiosource/openmpt/soloud_openmpt_dll.c
new file mode 100644
index 0000000..45ec003
--- /dev/null
+++ b/src/soloud/src/audiosource/openmpt/soloud_openmpt_dll.c
@@ -0,0 +1,132 @@
+/*
+Openmpt module for SoLoud audio engine
+Copyright (c) 2016 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+#if defined(_WIN32)||defined(_WIN64)
+#define WINDOWS_VERSION
+#endif // __WINDOWS__
+#include
+
+typedef void * (*dll_openmpt_module_create_from_memory)(const void * filedata, size_t filesize, void *logfunc, void * user, void * ctls);
+typedef void (*dll_openmpt_module_destroy)(void * mod);
+typedef int (*dll_openmpt_module_read_float_stereo)(void * mod, int samplerate, size_t count, float * left, float * right);
+
+static dll_openmpt_module_create_from_memory d_openmpt_module_create_from_memory = NULL;
+static dll_openmpt_module_destroy d_openmpt_module_destroy = NULL;
+static dll_openmpt_module_read_float_stereo d_openmpt_module_read_float_stereo = NULL;
+
+#ifdef WINDOWS_VERSION
+#include
+
+static HMODULE openDll()
+{
+ HMODULE res = LoadLibraryA("libopenmpt.dll");
+ return res;
+}
+
+static void* getDllProc(HMODULE aDllHandle, const char *aProcName)
+{
+ return GetProcAddress(aDllHandle, aProcName);
+}
+
+#elif defined(__vita__)
+
+static void * openDll()
+{
+ return NULL;
+}
+
+static void* getDllProc(void * aLibrary, const char *aProcName)
+{
+ return NULL;
+}
+
+#else
+#include // dll functions
+
+static void * openDll()
+{
+ void * res;
+ res = dlopen("libopenmpt.so", RTLD_LAZY);
+ return res;
+}
+
+static void* getDllProc(void * aLibrary, const char *aProcName)
+{
+ return dlsym(aLibrary, aProcName);
+}
+
+#endif
+
+static int load_dll()
+{
+#ifdef WINDOWS_VERSION
+ HMODULE dll = NULL;
+#else
+ void * dll = NULL;
+#endif
+
+ if (d_openmpt_module_create_from_memory != NULL)
+ {
+ return 1;
+ }
+
+ dll = openDll();
+
+ if (dll)
+ {
+ d_openmpt_module_create_from_memory = (dll_openmpt_module_create_from_memory)getDllProc(dll, "openmpt_module_create_from_memory");
+ d_openmpt_module_destroy = (dll_openmpt_module_destroy)getDllProc(dll, "openmpt_module_destroy");
+ d_openmpt_module_read_float_stereo = (dll_openmpt_module_read_float_stereo)getDllProc(dll, "openmpt_module_read_float_stereo");
+
+
+ if (d_openmpt_module_create_from_memory &&
+ d_openmpt_module_destroy &&
+ d_openmpt_module_read_float_stereo)
+ {
+ return 1;
+ }
+ }
+ d_openmpt_module_create_from_memory = NULL;
+ return 0;
+}
+
+void *openmpt_module_create_from_memory(const void * filedata, size_t filesize, void *logfunc, void * user, void * ctls)
+{
+ if (load_dll())
+ return d_openmpt_module_create_from_memory(filedata, filesize, logfunc, user, ctls);
+ return 0;
+}
+
+void openmpt_module_destroy(void * mod)
+{
+ if (load_dll())
+ d_openmpt_module_destroy(mod);
+}
+
+int openmpt_module_read_float_stereo(void * mod, int samplerate, size_t count, float * left, float * right)
+{
+ if (load_dll())
+ return d_openmpt_module_read_float_stereo(mod, samplerate, count, left, right);
+ return 0;
+}
diff --git a/src/soloud/src/audiosource/sfxr/soloud_sfxr.cpp b/src/soloud/src/audiosource/sfxr/soloud_sfxr.cpp
new file mode 100644
index 0000000..461f1a3
--- /dev/null
+++ b/src/soloud/src/audiosource/sfxr/soloud_sfxr.cpp
@@ -0,0 +1,633 @@
+/*
+SFXR module for SoLoud audio engine
+Copyright (c) 2014 Jari Komppa
+Based on code (c) by Tomas Pettersson, re-licensed under zlib by permission
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include
+#include "soloud_sfxr.h"
+#include "soloud_file.h"
+
+namespace SoLoud
+{
+
+ Prg::Prg()
+ {
+ }
+
+ void Prg::srand(int aSeed)
+ {
+ index = 0;
+ int i;
+ for (i = 0; i < 16; i++)
+ state[i] = aSeed + i * aSeed + i;
+ }
+
+ // WELL512 implementation, public domain by Chris Lomont
+ unsigned int Prg::rand()
+ {
+ unsigned int a, b, c, d;
+ a = state[index];
+ c = state[(index+13)&15];
+ b = a^c^(a<<16)^(c<<15);
+ c = state[(index+9)&15];
+ c ^= (c>>11);
+ a = state[index] = b^c;
+ d = a^((a<<5)&0xDA442D24UL);
+ index = (index + 15)&15;
+ a = state[index];
+ state[index] = a^b^d^(a<<2)^(b<<18)^(c<<28);
+ return state[index];
+ }
+
+ SfxrInstance::SfxrInstance(Sfxr *aParent)
+ {
+ mParent = aParent;
+ mParams = aParent->mParams;
+ mRand.srand(0x792352);
+ resetSample(false);
+ playing_sample = 1;
+ }
+
+#define frnd(x) ((float)(mRand.rand()%10001)/10000*(x))
+
+ unsigned int SfxrInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ float *buffer = aBuffer;
+ unsigned int i;
+ for (i = 0; i < aSamplesToRead; i++)
+ {
+ rep_time++;
+ if (rep_limit != 0 && rep_time >= rep_limit)
+ {
+ rep_time = 0;
+ resetSample(true);
+ }
+
+ // frequency envelopes/arpeggios
+ arp_time++;
+ if (arp_limit != 0 && arp_time >= arp_limit)
+ {
+ arp_limit = 0;
+ fperiod *= arp_mod;
+ }
+ fslide += fdslide;
+ fperiod *= fslide;
+ if (fperiod > fmaxperiod)
+ {
+ fperiod = fmaxperiod;
+ if (mParams.p_freq_limit > 0.0f)
+ {
+ if (mFlags & LOOPING)
+ {
+ resetSample(false);
+ }
+ else
+ {
+ playing_sample = false;
+ return i;
+ }
+ }
+ }
+ float rfperiod = (float)fperiod;
+ if (vib_amp > 0.0f)
+ {
+ vib_phase += vib_speed;
+ rfperiod = (float)(fperiod * (1.0 + sin(vib_phase) * vib_amp));
+ }
+ period = (int)rfperiod;
+ if (period < 8) period = 8;
+ square_duty += square_slide;
+ if (square_duty < 0.0f) square_duty = 0.0f;
+ if (square_duty > 0.5f) square_duty = 0.5f;
+ // volume envelope
+ env_time++;
+ if (env_time > env_length[env_stage])
+ {
+ env_time = 0;
+ env_stage++;
+ if (env_stage == 3)
+ {
+ if (mFlags & LOOPING)
+ {
+ resetSample(false);
+ }
+ else
+ {
+ playing_sample = false;
+ return i;
+ }
+ }
+ }
+ if (env_stage == 0)
+ {
+ if (env_length[0])
+ {
+ env_vol = (float)env_time / env_length[0];
+ }
+ else
+ {
+ env_vol = 0;
+ }
+ }
+ if (env_stage == 1)
+ {
+ if (env_length[1])
+ {
+ env_vol = 1.0f + (float)pow(1.0f - (float)env_time / env_length[1], 1.0f) * 2.0f * mParams.p_env_punch;
+ }
+ else
+ {
+ env_vol = 0;
+ }
+ }
+ if (env_stage == 2)
+ {
+ if (env_length[2])
+ {
+ env_vol = 1.0f - (float)env_time / env_length[2];
+ }
+ else
+ {
+ env_vol = 0;
+ }
+ }
+
+ // phaser step
+ fphase += fdphase;
+ iphase = abs((int)fphase);
+ if (iphase > 1023) iphase = 1023;
+
+ if (flthp_d != 0.0f)
+ {
+ flthp *= flthp_d;
+ if (flthp < 0.00001f) flthp = 0.00001f;
+ if (flthp > 0.1f) flthp = 0.1f;
+ }
+
+ float ssample = 0.0f;
+ for (int si = 0; si < 8; si++) // 8x supersampling
+ {
+ float sample = 0.0f;
+ phase++;
+ if (phase >= period)
+ {
+ phase %= period;
+ if (mParams.wave_type == 3)
+ {
+ for (int k = 0; k < 32; k++)
+ {
+ noise_buffer[k] = frnd(2.0f) - 1.0f;
+ }
+ }
+ }
+ // base waveform
+ float fp = (float)phase / period;
+ switch (mParams.wave_type)
+ {
+ case 0: // square
+ if (fp < square_duty)
+ {
+ sample = 0.5f;
+ }
+ else
+ {
+ sample = -0.5f;
+ }
+ break;
+ case 1: // sawtooth
+ sample = 1.0f - fp * 2;
+ break;
+ case 2: // sine
+ sample = (float)sin(fp * 2 * M_PI);
+ break;
+ case 3: // noise
+ sample = noise_buffer[phase * 32 / period];
+ break;
+ }
+ // lp filter
+ float pp = fltp;
+ fltw *= fltw_d;
+ if (fltw < 0.0f) fltw = 0.0f;
+ if (fltw > 0.1f) fltw = 0.1f;
+ if (mParams.p_lpf_freq != 1.0f)
+ {
+ fltdp += (sample - fltp) * fltw;
+ fltdp -= fltdp * fltdmp;
+ }
+ else
+ {
+ fltp = sample;
+ fltdp = 0.0f;
+ }
+ fltp += fltdp;
+ // hp filter
+ fltphp += fltp - pp;
+ fltphp -= fltphp * flthp;
+ sample = fltphp;
+ // phaser
+ phaser_buffer[ipp & 1023] = sample;
+ sample += phaser_buffer[(ipp - iphase + 1024) & 1023];
+ ipp = (ipp + 1) & 1023;
+ // final accumulation and envelope application
+ ssample += sample*env_vol;
+ }
+ ssample = ssample / 8 * mParams.master_vol;
+
+ ssample *= 2.0f * mParams.sound_vol;
+
+ if (buffer != NULL)
+ {
+ if (ssample > 1.0f) ssample = 1.0f;
+ if (ssample < -1.0f) ssample = -1.0f;
+ *buffer = ssample;
+ buffer++;
+ }
+ }
+ return aSamplesToRead;
+ }
+
+ bool SfxrInstance::hasEnded()
+ {
+ return !playing_sample;
+ }
+
+ void SfxrInstance::resetSample(bool aRestart)
+ {
+ if(!aRestart)
+ phase=0;
+ fperiod=100.0/(mParams.p_base_freq*mParams.p_base_freq+0.001);
+ period=(int)fperiod;
+ fmaxperiod=100.0/(mParams.p_freq_limit*mParams.p_freq_limit+0.001);
+ fslide=1.0-pow((double)mParams.p_freq_ramp, 3.0)*0.01;
+ fdslide=-pow((double)mParams.p_freq_dramp, 3.0)*0.000001;
+ square_duty=0.5f-mParams.p_duty*0.5f;
+ square_slide=-mParams.p_duty_ramp*0.00005f;
+ if(mParams.p_arp_mod>=0.0f)
+ arp_mod=1.0-pow((double)mParams.p_arp_mod, 2.0)*0.9;
+ else
+ arp_mod=1.0+pow((double)mParams.p_arp_mod, 2.0)*10.0;
+ arp_time=0;
+ arp_limit=(int)(pow(1.0f-mParams.p_arp_speed, 2.0f)*20000+32);
+ if(mParams.p_arp_speed==1.0f)
+ arp_limit=0;
+ if(!aRestart)
+ {
+ // reset filter
+ fltp=0.0f;
+ fltdp=0.0f;
+ fltw=(float)pow(mParams.p_lpf_freq, 3.0f)*0.1f;
+ fltw_d=1.0f+mParams.p_lpf_ramp*0.0001f;
+ fltdmp=5.0f/(1.0f+(float)pow(mParams.p_lpf_resonance, 2.0f)*20.0f)*(0.01f+fltw);
+ if(fltdmp>0.8f) fltdmp=0.8f;
+ fltphp=0.0f;
+ flthp=(float)pow(mParams.p_hpf_freq, 2.0f)*0.1f;
+ flthp_d=(float)(1.0+mParams.p_hpf_ramp*0.0003f);
+ // reset vibrato
+ vib_phase=0.0f;
+ vib_speed=(float)pow(mParams.p_vib_speed, 2.0f)*0.01f;
+ vib_amp=mParams.p_vib_strength*0.5f;
+ // reset envelope
+ env_vol=0.0f;
+ env_stage=0;
+ env_time=0;
+ env_length[0]=(int)(mParams.p_env_attack*mParams.p_env_attack*100000.0f);
+ env_length[1]=(int)(mParams.p_env_sustain*mParams.p_env_sustain*100000.0f);
+ env_length[2]=(int)(mParams.p_env_decay*mParams.p_env_decay*100000.0f);
+
+ fphase=(float)pow(mParams.p_pha_offset, 2.0f)*1020.0f;
+ if(mParams.p_pha_offset<0.0f) fphase=-fphase;
+ fdphase=(float)pow(mParams.p_pha_ramp, 2.0f)*1.0f;
+ if(mParams.p_pha_ramp<0.0f) fdphase=-fdphase;
+ iphase=abs((int)fphase);
+ ipp=0;
+ for(int i=0;i<1024;i++)
+ phaser_buffer[i]=0.0f;
+
+ for(int i=0;i<32;i++)
+ noise_buffer[i]=frnd(2.0f)-1.0f;
+
+ rep_time=0;
+ rep_limit=(int)(pow(1.0f-mParams.p_repeat_speed, 2.0f)*20000+32);
+ if(mParams.p_repeat_speed==0.0f)
+ rep_limit=0;
+ }
+ }
+
+
+#define rnd(n) (mRand.rand()%((n)+1))
+#undef frnd
+#define frnd(x) ((float)(mRand.rand()%10001)/10000*(x))
+
+
+ result Sfxr::loadPreset(int aPresetNo, int aRandSeed)
+ {
+ if (aPresetNo < 0 || aPresetNo > 6)
+ return INVALID_PARAMETER;
+
+ resetParams();
+ mRand.srand(aRandSeed);
+ switch(aPresetNo)
+ {
+ case 0: // pickup/coin
+ mParams.p_base_freq=0.4f+frnd(0.5f);
+ mParams.p_env_attack=0.0f;
+ mParams.p_env_sustain=frnd(0.1f);
+ mParams.p_env_decay=0.1f+frnd(0.4f);
+ mParams.p_env_punch=0.3f+frnd(0.3f);
+ if(rnd(1))
+ {
+ mParams.p_arp_speed=0.5f+frnd(0.2f);
+ mParams.p_arp_mod=0.2f+frnd(0.4f);
+ }
+ break;
+ case 1: // laser/shoot
+ mParams.wave_type=rnd(2);
+ if(mParams.wave_type==2 && rnd(1))
+ mParams.wave_type=rnd(1);
+ mParams.p_base_freq=0.5f+frnd(0.5f);
+ mParams.p_freq_limit=mParams.p_base_freq-0.2f-frnd(0.6f);
+ if(mParams.p_freq_limit<0.2f) mParams.p_freq_limit=0.2f;
+ mParams.p_freq_ramp=-0.15f-frnd(0.2f);
+ if(rnd(2)==0)
+ {
+ mParams.p_base_freq=0.3f+frnd(0.6f);
+ mParams.p_freq_limit=frnd(0.1f);
+ mParams.p_freq_ramp=-0.35f-frnd(0.3f);
+ }
+ if(rnd(1))
+ {
+ mParams.p_duty=frnd(0.5f);
+ mParams.p_duty_ramp=frnd(0.2f);
+ }
+ else
+ {
+ mParams.p_duty=0.4f+frnd(0.5f);
+ mParams.p_duty_ramp=-frnd(0.7f);
+ }
+ mParams.p_env_attack=0.0f;
+ mParams.p_env_sustain=0.1f+frnd(0.2f);
+ mParams.p_env_decay=frnd(0.4f);
+ if(rnd(1))
+ mParams.p_env_punch=frnd(0.3f);
+ if(rnd(2)==0)
+ {
+ mParams.p_pha_offset=frnd(0.2f);
+ mParams.p_pha_ramp=-frnd(0.2f);
+ }
+ if(rnd(1))
+ mParams.p_hpf_freq=frnd(0.3f);
+ break;
+ case 2: // explosion
+ mParams.wave_type=3;
+ if(rnd(1))
+ {
+ mParams.p_base_freq=0.1f+frnd(0.4f);
+ mParams.p_freq_ramp=-0.1f+frnd(0.4f);
+ }
+ else
+ {
+ mParams.p_base_freq=0.2f+frnd(0.7f);
+ mParams.p_freq_ramp=-0.2f-frnd(0.2f);
+ }
+ mParams.p_base_freq*=mParams.p_base_freq;
+ if(rnd(4)==0)
+ mParams.p_freq_ramp=0.0f;
+ if(rnd(2)==0)
+ mParams.p_repeat_speed=0.3f+frnd(0.5f);
+ mParams.p_env_attack=0.0f;
+ mParams.p_env_sustain=0.1f+frnd(0.3f);
+ mParams.p_env_decay=frnd(0.5f);
+ if(rnd(1)==0)
+ {
+ mParams.p_pha_offset=-0.3f+frnd(0.9f);
+ mParams.p_pha_ramp=-frnd(0.3f);
+ }
+ mParams.p_env_punch=0.2f+frnd(0.6f);
+ if(rnd(1))
+ {
+ mParams.p_vib_strength=frnd(0.7f);
+ mParams.p_vib_speed=frnd(0.6f);
+ }
+ if(rnd(2)==0)
+ {
+ mParams.p_arp_speed=0.6f+frnd(0.3f);
+ mParams.p_arp_mod=0.8f-frnd(1.6f);
+ }
+ break;
+ case 3: // powerup
+ if(rnd(1))
+ mParams.wave_type=1;
+ else
+ mParams.p_duty=frnd(0.6f);
+ if(rnd(1))
+ {
+ mParams.p_base_freq=0.2f+frnd(0.3f);
+ mParams.p_freq_ramp=0.1f+frnd(0.4f);
+ mParams.p_repeat_speed=0.4f+frnd(0.4f);
+ }
+ else
+ {
+ mParams.p_base_freq=0.2f+frnd(0.3f);
+ mParams.p_freq_ramp=0.05f+frnd(0.2f);
+ if(rnd(1))
+ {
+ mParams.p_vib_strength=frnd(0.7f);
+ mParams.p_vib_speed=frnd(0.6f);
+ }
+ }
+ mParams.p_env_attack=0.0f;
+ mParams.p_env_sustain=frnd(0.4f);
+ mParams.p_env_decay=0.1f+frnd(0.4f);
+ break;
+ case 4: // hit/hurt
+ mParams.wave_type=rnd(2);
+ if(mParams.wave_type==2)
+ mParams.wave_type=3;
+ if(mParams.wave_type==0)
+ mParams.p_duty=frnd(0.6f);
+ mParams.p_base_freq=0.2f+frnd(0.6f);
+ mParams.p_freq_ramp=-0.3f-frnd(0.4f);
+ mParams.p_env_attack=0.0f;
+ mParams.p_env_sustain=frnd(0.1f);
+ mParams.p_env_decay=0.1f+frnd(0.2f);
+ if(rnd(1))
+ mParams.p_hpf_freq=frnd(0.3f);
+ break;
+ case 5: // jump
+ mParams.wave_type=0;
+ mParams.p_duty=frnd(0.6f);
+ mParams.p_base_freq=0.3f+frnd(0.3f);
+ mParams.p_freq_ramp=0.1f+frnd(0.2f);
+ mParams.p_env_attack=0.0f;
+ mParams.p_env_sustain=0.1f+frnd(0.3f);
+ mParams.p_env_decay=0.1f+frnd(0.2f);
+ if(rnd(1))
+ mParams.p_hpf_freq=frnd(0.3f);
+ if(rnd(1))
+ mParams.p_lpf_freq=1.0f-frnd(0.6f);
+ break;
+ case 6: // blip/select
+ mParams.wave_type=rnd(1);
+ if(mParams.wave_type==0)
+ mParams.p_duty=frnd(0.6f);
+ mParams.p_base_freq=0.2f+frnd(0.4f);
+ mParams.p_env_attack=0.0f;
+ mParams.p_env_sustain=0.1f+frnd(0.1f);
+ mParams.p_env_decay=frnd(0.2f);
+ mParams.p_hpf_freq=0.1f;
+ break;
+ }
+ return 0;
+ }
+
+ void Sfxr::resetParams()
+ {
+ mParams.wave_type=0;
+
+ mParams.p_base_freq=0.3f;
+ mParams.p_freq_limit=0.0f;
+ mParams.p_freq_ramp=0.0f;
+ mParams.p_freq_dramp=0.0f;
+ mParams.p_duty=0.0f;
+ mParams.p_duty_ramp=0.0f;
+
+ mParams.p_vib_strength=0.0f;
+ mParams.p_vib_speed=0.0f;
+ mParams.p_vib_delay=0.0f;
+
+ mParams.p_env_attack=0.0f;
+ mParams.p_env_sustain=0.3f;
+ mParams.p_env_decay=0.4f;
+ mParams.p_env_punch=0.0f;
+
+ mParams.filter_on=false;
+ mParams.p_lpf_resonance=0.0f;
+ mParams.p_lpf_freq=1.0f;
+ mParams.p_lpf_ramp=0.0f;
+ mParams.p_hpf_freq=0.0f;
+ mParams.p_hpf_ramp=0.0f;
+
+ mParams.p_pha_offset=0.0f;
+ mParams.p_pha_ramp=0.0f;
+
+ mParams.p_repeat_speed=0.0f;
+
+ mParams.p_arp_speed=0.0f;
+ mParams.p_arp_mod=0.0f;
+
+ mParams.master_vol=0.05f;
+ mParams.sound_vol=0.5f;
+ }
+
+ result Sfxr::loadParamsMem(unsigned char *aMem, unsigned int aLength, bool aCopy, bool aTakeOwnership)
+ {
+ MemoryFile mf;
+ int res = mf.openMem(aMem, aLength, aCopy, aTakeOwnership);
+ if (res != SO_NO_ERROR)
+ return res;
+ return loadParamsFile(&mf);
+ }
+
+ result Sfxr::loadParams(const char *aFilename)
+ {
+ DiskFile df;
+ int res = df.open(aFilename);
+ if (res != SO_NO_ERROR)
+ return res;
+ return loadParamsFile(&df);
+ }
+
+ result Sfxr::loadParamsFile(File *aFile)
+ {
+ int version=0;
+ aFile->read((unsigned char*)&version, sizeof(int));
+ if(version!=100 && version!=101 && version!=102)
+ {
+ return FILE_LOAD_FAILED;
+ }
+
+ aFile->read((unsigned char*)&mParams.wave_type, sizeof(int));
+
+
+ mParams.sound_vol=0.5f;
+ if(version==102)
+ aFile->read((unsigned char*)&mParams.sound_vol, sizeof(float));
+
+ aFile->read((unsigned char*)&mParams.p_base_freq, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_freq_limit, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_freq_ramp, sizeof(float));
+ if(version>=101)
+ aFile->read((unsigned char*)&mParams.p_freq_dramp, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_duty, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_duty_ramp, sizeof(float));
+
+ aFile->read((unsigned char*)&mParams.p_vib_strength, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_vib_speed, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_vib_delay, sizeof(float));
+
+ aFile->read((unsigned char*)&mParams.p_env_attack, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_env_sustain, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_env_decay, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_env_punch, sizeof(float));
+
+ aFile->read((unsigned char*)&mParams.filter_on, sizeof(bool));
+ aFile->read((unsigned char*)&mParams.p_lpf_resonance, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_lpf_freq, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_lpf_ramp, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_hpf_freq, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_hpf_ramp, sizeof(float));
+
+ aFile->read((unsigned char*)&mParams.p_pha_offset, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_pha_ramp, sizeof(float));
+
+ aFile->read((unsigned char*)&mParams.p_repeat_speed, sizeof(float));
+
+ if(version>=101)
+ {
+ aFile->read((unsigned char*)&mParams.p_arp_speed, sizeof(float));
+ aFile->read((unsigned char*)&mParams.p_arp_mod, sizeof(float));
+ }
+
+ return 0;
+ }
+
+ Sfxr::~Sfxr()
+ {
+ stop();
+ }
+
+ Sfxr::Sfxr()
+ {
+ resetParams();
+ mBaseSamplerate = 44100;
+ }
+
+
+ AudioSourceInstance * Sfxr::createInstance()
+ {
+ return new SfxrInstance(this);
+ }
+
+};
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/speech/Elements.def b/src/soloud/src/audiosource/speech/Elements.def
new file mode 100644
index 0000000..cdeabbd
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/Elements.def
@@ -0,0 +1,1659 @@
+/*mName, mRK, mDU, mUD, mFont, mDict, mIpa, mFeat, interpolators*/
+/* (mSteady, mFixed, mProportion, mExtDelay, mIntDelay) */
+{"END", 31, 5, 5,0x00,NULL,NULL,0,
+ {
+ { 270, 135, 50, 3, 3}, /* ELM_FN 0 */
+ { 490, 0, 100, 0, 0}, /* ELM_F1 0 */
+ { 1480, 0, 100, 0, 0}, /* ELM_F2 0 */
+ { 2500, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 0}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_AN -10.5 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_A1 -10.5 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_A2 -10.5 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_A3 -10.5 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_A4 -10.5 */
+ { -30, 0, 100, 3, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"Q", 29, 6, 6,0x00,NULL,NULL,0,
+ {
+ { 270, 135, 50, 3, 3}, /* ELM_FN 0 */
+ { 490, 0, 100, 3, 3}, /* ELM_F1 0 */
+ { 1480, 0, 100, 3, 3}, /* ELM_F2 0 */
+ { 2500, 0, 100, 3, 3}, /* ELM_F3 0 */
+ { 60, 0, 100, 3, 3}, /* ELM_B1 0 */
+ { 90, 0, 100, 3, 3}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 3}, /* ELM_B3 0 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_AN -10.5 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_A1 -10.5 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_A2 -10.5 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_A3 -10.5 */
+ { -30, -10.5, 100, 3, 0}, /* ELM_A4 -10.5 */
+ { -30, 0, 100, 3, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"P", 23, 8, 8,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 2}, /* ELM_F1 15 */
+ { 760, 350, 50, 2, 2}, /* ELM_F2 -30 */
+ { 2500, 0, 100, 0, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"PY", 29, 1, 1,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 0, 100, 0, 0}, /* ELM_F1 0 */
+ { 760, 0, 100, 0, 0}, /* ELM_F2 0 */
+ { 2500, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 0}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 49, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 43.75, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 38.5, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"PZ", 23, 2, 2,0x70,"p","p",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 2}, /* ELM_F1 15 */
+ { 760, 350, 50, 2, 2}, /* ELM_F2 -30 */
+ { 2500, 0, 100, 2, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 2, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 38.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 33.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 28, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"T", 23, 6, 6,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 2}, /* ELM_F1 15 */
+ { 1780, 950, 50, 2, 2}, /* ELM_F2 60 */
+ { 2680, 2680, 0, 0, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 2}, /* ELM_B2 0 */
+ { 150, 150, 0, 0, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"TY", 29, 1, 1,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 0, 100, 0, 0}, /* ELM_F1 0 */
+ { 1780, 0, 100, 0, 0}, /* ELM_F2 0 */
+ { 2680, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 0}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 38.5, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 50.75, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"TZ", 23, 2, 2,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 1}, /* ELM_F1 15 */
+ { 1780, 950, 50, 2, 1}, /* ELM_F2 60 */
+ { 2680, 2680, 0, 2, 0}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 1}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 1}, /* ELM_B2 0 */
+ { 150, 150, 0, 2, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 28, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"K", 23, 8, 8,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 3, 3}, /* ELM_F1 15 */
+ { 1480, 1550, 50, 3, 3}, /* ELM_F2 810 */
+ { 2620, 1580, 50, 3, 3}, /* ELM_F3 270 */
+ { 60, 30, 50, 3, 3}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 3}, /* ELM_B2 0 */
+ { 150, 75, 50, 3, 3}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"KY", 29, 1, 1,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 0, 100, 0, 0}, /* ELM_F1 0 */
+ { 1480, 0, 100, 0, 0}, /* ELM_F2 0 */
+ { 2620, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 0}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 50.75, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 50.75, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"KZ", 23, 4, 4,0x6B,"k","k",ELM_FEATURE_STP|ELM_FEATURE_VEL|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 3, 3}, /* ELM_F1 15 */
+ { 1480, 1550, 50, 3, 3}, /* ELM_F2 810 */
+ { 2620, 1580, 50, 3, 3}, /* ELM_F3 270 */
+ { 60, 30, 50, 3, 3}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 3}, /* ELM_B2 0 */
+ { 150, 75, 50, 3, 3}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 19.25, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"B", 26,12,12,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 2}, /* ELM_F1 15 */
+ { 760, 350, 50, 2, 2}, /* ELM_F2 -30 */
+ { 2500, 0, 100, 0, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"BY", 29, 1, 1,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 0, 100, 0, 0}, /* ELM_F1 0 */
+ { 760, 0, 100, 0, 0}, /* ELM_F2 0 */
+ { 2500, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 0}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 49, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 43.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 38.5, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"BZ", 26, 0, 0,0x62,"b","b",ELM_FEATURE_BLB|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 0}, /* ELM_F1 15 */
+ { 760, 350, 50, 2, 0}, /* ELM_F2 -30 */
+ { 2500, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 0}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"D", 26, 8, 8,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 2}, /* ELM_F1 15 */
+ { 1780, 950, 50, 2, 2}, /* ELM_F2 60 */
+ { 2680, 2680, 0, 2, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 2}, /* ELM_B2 0 */
+ { 150, 150, 0, 2, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 31.5, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"DY", 29, 1, 1,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 0, 100, 0, 0}, /* ELM_F1 0 */
+ { 1780, 0, 100, 0, 0}, /* ELM_F2 0 */
+ { 2680, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 0}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 38.5, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 38.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 35, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 45.5, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"DZ", 26, 1, 1,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 0}, /* ELM_F1 15 */
+ { 1780, 950, 50, 2, 0}, /* ELM_F2 60 */
+ { 2680, 2680, 0, 2, 0}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 0}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 0}, /* ELM_B2 0 */
+ { 150, 150, 0, 2, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 38.5, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 28, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 35, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+
+{"G", 26,12,12,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 3, 3}, /* ELM_F1 15 */
+ { 1480, 1550, 50, 3, 3}, /* ELM_F2 810 */
+ { 2620, 1580, 50, 3, 3}, /* ELM_F3 270 */
+ { 60, 30, 50, 3, 3}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 3}, /* ELM_B2 0 */
+ { 150, 75, 50, 3, 3}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 35, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"GY", 29, 1, 1,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 0, 100, 0, 0}, /* ELM_F1 0 */
+ { 1480, 0, 100, 0, 0}, /* ELM_F2 0 */
+ { 2620, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 0}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 35, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 45.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"GZ", 26, 2, 2,0x67,"g","g",ELM_FEATURE_STP|ELM_FEATURE_VCD|ELM_FEATURE_VEL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 3, 2}, /* ELM_F1 15 */
+ { 1480, 1550, 50, 3, 2}, /* ELM_F2 810 */
+ { 2620, 1580, 50, 3, 2}, /* ELM_F3 270 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 75, 50, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 35, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 35, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 14, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"M", 15, 8, 8,0x6D,"m","m",ELM_FEATURE_BLB|ELM_FEATURE_NAS,
+ {
+ { 360, 360, 0, 3, 0}, /* ELM_FN 0 */
+ { 480, 480, 0, 3, 0}, /* ELM_F1 0 */
+ { 1000, 350, 50, 3, 0}, /* ELM_F2 -150 */
+ { 2200, 0, 100, 5, 0}, /* ELM_F3 0 */
+ { 40, 20, 50, 3, 0}, /* ELM_B1 0 */
+ { 175, 87, 50, 3, 0}, /* ELM_B2 -0.5 */
+ { 120, 0, 100, 5, 0}, /* ELM_B3 0 */
+ { 42, 21, 50, 3, 0}, /* ELM_AN 0 */
+ { 26, -10, 100, 3, 0}, /* ELM_A1 -10 */
+ { 30, -10, 100, 3, 0}, /* ELM_A2 -10 */
+ { 33, -10, 100, 3, 0}, /* ELM_A3 -10 */
+ { -30, -10, 100, 3, 0}, /* ELM_A4 -10 */
+ { -30, 0, 100, 3, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 2, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 2, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 2, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 2, 0} /* ELM_AF 0 */
+ }
+},
+
+{"N", 15, 8, 8,0x6E,"n","n",ELM_FEATURE_ALV|ELM_FEATURE_NAS,
+ {
+ { 450, 450, 0, 3, 0}, /* ELM_FN 0 */
+ { 480, 480, 0, 3, 0}, /* ELM_F1 0 */
+ { 1780, 950, 50, 3, 3}, /* ELM_F2 60 */
+ { 2620, 2680, 0, 3, 0}, /* ELM_F3 60 */
+ { 40, 20, 50, 3, 0}, /* ELM_B1 0 */
+ { 300, 150, 50, 3, 3}, /* ELM_B2 0 */
+ { 260, 130, 50, 3, 0}, /* ELM_B3 0 */
+ { 42, 21, 50, 3, 0}, /* ELM_AN 0 */
+ { 35, -10, 100, 3, 0}, /* ELM_A1 -10 */
+ { 35, -10, 100, 3, 0}, /* ELM_A2 -10 */
+ { 35, -10, 100, 3, 0}, /* ELM_A3 -10 */
+ { 20, -10, 100, 3, 0}, /* ELM_A4 -10 */
+ { -30, 0, 100, 3, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 2, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 2, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 2, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 2, 0} /* ELM_AF 0 */
+ }
+},
+
+{"NG", 15, 8, 8,0x4E,"N","N",ELM_FEATURE_NAS|ELM_FEATURE_VEL,
+ {
+ { 360, 360, 0, 3, 0}, /* ELM_FN 0 */
+ { 480, 480, 0, 3, 0}, /* ELM_F1 0 */
+ { 820, 1550, 50, 5, 3}, /* ELM_F2 1140 */
+ { 2800, 1580, 50, 3, 3}, /* ELM_F3 180 */
+ { 160, 80, 0, 5, 0}, /* ELM_B1 -80 */
+ { 150, 75, 50, 5, 3}, /* ELM_B2 0 */
+ { 100, 50, 50, 3, 0}, /* ELM_B3 0 */
+ { 42, 21, 50, 3, 3}, /* ELM_AN 0 */
+ { 20, 0, 100, 3, 0}, /* ELM_A1 0 */
+ { 30, 0, 100, 3, 0}, /* ELM_A2 0 */
+ { 35, 0, 100, 3, 0}, /* ELM_A3 0 */
+ { 0, 0, 100, 3, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 3, 0}, /* ELM_AB 0 */
+ { 52, 26, 50, 2, 0}, /* ELM_AV 0 */
+ { 56, 28, 50, 2, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 2, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 2, 0} /* ELM_AF 0 */
+ }
+},
+
+{"F", 18,12,12,0x66,"f","f",ELM_FEATURE_FRC|ELM_FEATURE_LBD|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */
+ { 1420, 350, 50, 3, 2}, /* ELM_F2 -360 */
+ { 2560, 980, 50, 3, 2}, /* ELM_F3 -300 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 75, 50, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 0, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 0, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { 54, 27, 50, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 32, 16, 50, 0, 0}, /* ELM_ASP 0 */
+ { 54, 30, 50, 0, 0} /* ELM_AF 3 */
+ }
+},
+
+{"TH", 18,15,15,0x54,"T","T",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */
+ { 1780, 1190, 50, 3, 2}, /* ELM_F2 300 */
+ { 2680, 2680, 0, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 150, 0, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 28, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 22.75, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"S", 18,12,12,0x73,"s","s",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */
+ { 1720, 950, 50, 3, 2}, /* ELM_F2 90 */
+ { 2620, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 200, 100, 50, 3, 2}, /* ELM_B1 0 */
+ { 96, 48, 50, 3, 2}, /* ELM_B2 0 */
+ { 220, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 28, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 28, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 32, 16, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"SH", 18,12,12,0x53,"S","S",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */
+ { 2200, 1190, 50, 3, 2}, /* ELM_F2 90 */
+ { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 31.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 42, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 31.5, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"X", 18,12,12,0x78,"x","x",ELM_FEATURE_FRC|ELM_FEATURE_VEL|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 3, 3}, /* ELM_F1 15 */
+ { 1480, 1550, 50, 3, 3}, /* ELM_F2 810 */
+ { 2620, 1580, 50, 3, 3}, /* ELM_F3 270 */
+ { 60, 30, 50, 3, 3}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 3}, /* ELM_B2 0 */
+ { 150, 75, 50, 3, 3}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 19.25, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"H", 9,10,10,0x68,"h","h",ELM_FEATURE_APR|ELM_FEATURE_GLT,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 0, 100, 0, 7}, /* ELM_F1 0 */
+ { 1480, 0, 100, 0, 7}, /* ELM_F2 0 */
+ { 2500, 0, 100, 0, 7}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 7}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 7}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 7}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 7}, /* ELM_AN 0 */
+ { 35, -14, 100, 0, 7}, /* ELM_A1 -14 */
+ { 36.75, -14, 100, 0, 7}, /* ELM_A2 -14 */
+ { 26.25, -7, 100, 0, 7}, /* ELM_A3 -7 */
+ { 22.75, -3.5, 100, 0, 7}, /* ELM_A4 -3.5 */
+ { -30, 0, 100, 0, 7}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 7}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 7}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"V", 20, 4, 4,0x76,"v","v",ELM_FEATURE_FRC|ELM_FEATURE_LBD|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 280, 170, 50, 3, 2}, /* ELM_F1 30 */
+ { 1420, 350, 50, 3, 2}, /* ELM_F2 -360 */
+ { 2560, 980, 50, 3, 2}, /* ELM_F3 -300 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 75, 50, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 36.75, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 33.25, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"QQ", 30, 0, 0,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 280, 0, 100, 0, 0}, /* ELM_F1 0 */
+ { 1420, 0, 100, 0, 0}, /* ELM_F2 0 */
+ { 2560, 0, 100, 0, 0}, /* ELM_F3 0 */
+ { 60, 0, 100, 0, 0}, /* ELM_B1 0 */
+ { 90, 0, 100, 0, 0}, /* ELM_B2 0 */
+ { 150, 0, 100, 0, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 40.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 36.75, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 33.25, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"DH", 20, 4, 4,0x54,"D","D",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 280, 170, 50, 3, 2}, /* ELM_F1 30 */
+ { 1600, 1190, 50, 3, 2}, /* ELM_F2 390 */
+ { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 31.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 28, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { 54, 27, 50, 0, 0}, /* ELM_AB 0 */
+ { 36, 18, 50, 0, 0}, /* ELM_AV 0 */
+ { 54, 27, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"DI", 20, 4, 4,0x54,"D","D",ELM_FEATURE_DNT|ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 280, 170, 50, 3, 2}, /* ELM_F1 30 */
+ { 1600, 1190, 50, 3, 2}, /* ELM_F2 390 */
+ { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 31.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 28, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"Z", 20, 4, 4,0x7A,"z","z",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 280, 170, 50, 3, 2}, /* ELM_F1 30 */
+ { 1720, 950, 50, 3, 2}, /* ELM_F2 90 */
+ { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 36.75, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 40, 20, 50, 0, 0}, /* ELM_AV 0 */
+ { 54, 27, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"ZZ", 20, 4, 4,0x7A,"z","z",ELM_FEATURE_ALV|ELM_FEATURE_FRC|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 280, 170, 50, 3, 2}, /* ELM_F1 30 */
+ { 1720, 950, 50, 3, 2}, /* ELM_F2 90 */
+ { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 24.5, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 36.75, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"ZH", 20, 4, 4,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 280, 170, 50, 3, 2}, /* ELM_F1 30 */
+ { 2020, 1190, 50, 3, 2}, /* ELM_F2 180 */
+ { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 36.75, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"CH", 23, 4, 4,0x74,"t","t",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 2}, /* ELM_F1 15 */
+ { 1780, 950, 50, 2, 2}, /* ELM_F2 60 */
+ { 2680, 2680, 0, 2, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 2}, /* ELM_B2 0 */
+ { 150, 150, 0, 2, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"CI", 18, 8, 8,0x53,"S","S",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VLS,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 400, 170, 50, 3, 2}, /* ELM_F1 -30 */
+ { 2020, 1190, 50, 3, 2}, /* ELM_F2 180 */
+ { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 31.5, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 42, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 31.5, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AV 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_AVC 0 */
+ { 60, 30, 50, 0, 0}, /* ELM_ASP 0 */
+ { 60, 30, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"J", 26, 4, 4,0x64,"d","d",ELM_FEATURE_ALV|ELM_FEATURE_STP|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 110, 50, 2, 2}, /* ELM_F1 15 */
+ { 1780, 950, 50, 2, 2}, /* ELM_F2 60 */
+ { 2680, 2680, 0, 2, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 2, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 2, 2}, /* ELM_B2 0 */
+ { 150, 150, 0, 2, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 31.5, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"JY", 20, 3, 3,0x5A,"Z","Z",ELM_FEATURE_FRC|ELM_FEATURE_PLA|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 280, 170, 50, 3, 2}, /* ELM_F1 30 */
+ { 2020, 1190, 50, 3, 2}, /* ELM_F2 180 */
+ { 2560, 0, 100, 3, 2}, /* ELM_F3 0 */
+ { 60, 30, 50, 3, 2}, /* ELM_B1 0 */
+ { 90, 45, 50, 3, 2}, /* ELM_B2 0 */
+ { 150, 0, 100, 3, 2}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 29.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 36.75, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"L", 11, 8, 8,0x6C,"l","l",ELM_FEATURE_ALV|ELM_FEATURE_LAT|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 460, 230, 50, 6, 0}, /* ELM_F1 0 */
+ { 1480, 710, 50, 6, 0}, /* ELM_F2 -30 */
+ { 2500, 1220, 50, 6, 0}, /* ELM_F3 -30 */
+ { 60, 30, 50, 6, 0}, /* ELM_B1 0 */
+ { 90, 45, 50, 6, 0}, /* ELM_B2 0 */
+ { 150, 75, 50, 6, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 36.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 21, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"LL", 11, 8, 8,0x6C,"l","l",ELM_FEATURE_ALV|ELM_FEATURE_LAT|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 460, 230, 50, 6, 0}, /* ELM_F1 0 */
+ { 940, 470, 50, 6, 0}, /* ELM_F2 0 */
+ { 2500, 1220, 50, 6, 0}, /* ELM_F3 -30 */
+ { 60, 30, 50, 6, 0}, /* ELM_B1 0 */
+ { 90, 45, 50, 6, 0}, /* ELM_B2 0 */
+ { 150, 75, 50, 6, 0}, /* ELM_B3 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AN 0 */
+ { 36.75, 0, 100, 0, 0}, /* ELM_A1 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A2 0 */
+ { 26.25, 0, 100, 0, 0}, /* ELM_A3 0 */
+ { 21, 0, 100, 0, 0}, /* ELM_A4 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A5 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_A6 0 */
+ { -30, 0, 100, 0, 0}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"RX", 10,10,10,0xD5,"R","",ELM_FEATURE_RZD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 0, 100, 0, 5}, /* ELM_F1 0 */
+ { 1180, 0, 100, 0, 5}, /* ELM_F2 0 */
+ { 1600, 1600, 0, 5, 5}, /* ELM_F3 0 */
+ { 60, 30, 50, 0, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 70, 35, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 42, 21, 50, 5, 5}, /* ELM_A1 0 */
+ { 35, 17.5, 50, 5, 5}, /* ELM_A2 0 */
+ { 35, 17.5, 50, 5, 5}, /* ELM_A3 0 */
+ { -30, 0, 50, 5, 5}, /* ELM_A4 15 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 50, 25, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"R", 10,11,11,0xA8,"r","r",ELM_FEATURE_ALV|ELM_FEATURE_APR,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 0, 100, 0, 5}, /* ELM_F1 0 */
+ { 1180, 590, 50, 5, 5}, /* ELM_F2 0 */
+ { 1600, 740, 50, 5, 5}, /* ELM_F3 -60 */
+ { 60, 0, 100, 0, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 42, 21, 50, 5, 5}, /* ELM_A1 0 */
+ { 35, 17.5, 50, 5, 5}, /* ELM_A2 0 */
+ { 35, 17.5, 50, 5, 5}, /* ELM_A3 0 */
+ { -30, 0, 50, 5, 5}, /* ELM_A4 15 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"W", 10, 8, 8,0x77,"w","w",ELM_FEATURE_APR|ELM_FEATURE_LBV|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 190, 50, 50, 4, 4}, /* ELM_F1 -45 */
+ { 760, 350, 50, 4, 4}, /* ELM_F2 -30 */
+ { 2020, 980, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 43.75, 21, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 28, 14, 50, 4, 4}, /* ELM_A2 0 */
+ { 21, 10.5, 50, 4, 4}, /* ELM_A3 0 */
+ { -30, 0, 50, 4, 4}, /* ELM_A4 15 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"Y", 10, 7, 7,0x6A,"j","j",ELM_FEATURE_APR|ELM_FEATURE_PAL|ELM_FEATURE_VCD,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 250, 110, 50, 4, 4}, /* ELM_F1 -15 */
+ { 2500, 1190, 50, 4, 4}, /* ELM_F2 -60 */
+ { 2980, 1460, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 33.25, 17.5, 50, 4, 4}, /* ELM_A2 0.875 */
+ { 38.5, 17.5, 50, 4, 4}, /* ELM_A3 -1.75 */
+ { 31.5, 14, 50, 4, 4}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"I", 2, 8, 6,0x49,"I","I",ELM_FEATURE_FNT|ELM_FEATURE_SMH|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 400, 170, 50, 4, 4}, /* ELM_F1 -30 */
+ { 2080, 1070, 50, 4, 4}, /* ELM_F2 30 */
+ { 2560, 1340, 50, 4, 4}, /* ELM_F3 60 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 36.75, 17.5, 50, 4, 4}, /* ELM_A2 -0.875 */
+ { 35, 17.5, 50, 4, 4}, /* ELM_A3 0 */
+ { 29.75, 14, 50, 4, 4}, /* ELM_A4 -0.875 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"E", 2, 8, 4,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 640, 350, 50, 4, 4}, /* ELM_F1 30 */
+ { 2020, 1070, 50, 4, 4}, /* ELM_F2 60 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 42, 21, 50, 4, 4}, /* ELM_A2 0 */
+ { 38.5, 17.5, 50, 4, 4}, /* ELM_A3 -1.75 */
+ { 31.5, 14, 50, 4, 4}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"AA", 2,10, 5,0x51,"&","&",ELM_FEATURE_FNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 790, 410, 50, 4, 4}, /* ELM_F1 15 */
+ { 1780, 950, 50, 4, 4}, /* ELM_F2 60 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 130, 65, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 47.25, 24.5, 50, 4, 4}, /* ELM_A2 0.875 */
+ { 38.5, 17.5, 50, 4, 4}, /* ELM_A3 -1.75 */
+ { 31.5, 14, 50, 4, 4}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"U", 2, 9, 6,0xC3,"V","V",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 700, 350, 50, 4, 4}, /* ELM_F1 0 */
+ { 1360, 710, 50, 4, 4}, /* ELM_F2 30 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 43.75, 21, 50, 4, 4}, /* ELM_A2 -0.875 */
+ { 31.5, 14, 50, 4, 4}, /* ELM_A3 -1.75 */
+ { 24.5, 10.5, 50, 4, 4}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"O", 2, 9, 6,0x81,"0","A.",ELM_FEATURE_BCK|ELM_FEATURE_LOW|ELM_FEATURE_RND|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 610, 290, 50, 4, 4}, /* ELM_F1 -15 */
+ { 880, 470, 50, 4, 4}, /* ELM_F2 30 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 47.25, 24.5, 50, 4, 4}, /* ELM_A2 0.875 */
+ { 22.75, 10.5, 50, 4, 4}, /* ELM_A3 -0.875 */
+ { 15.75, 7, 50, 4, 4}, /* ELM_A4 -0.875 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"OO", 2, 6, 4,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 370, 170, 50, 4, 4}, /* ELM_F1 -15 */
+ { 1000, 470, 50, 4, 4}, /* ELM_F2 -30 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 42, 21, 50, 4, 4}, /* ELM_A2 0 */
+ { 28, 14, 50, 4, 4}, /* ELM_A3 0 */
+ { 22.75, 10.5, 50, 4, 4}, /* ELM_A4 -0.875 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"A", 2, 4, 4,0xAB,"@","@",ELM_FEATURE_CNT|ELM_FEATURE_MDL|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 230, 50, 4, 4}, /* ELM_F1 -15 */
+ { 1480, 710, 50, 4, 4}, /* ELM_F2 -30 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A2 -0.875 */
+ { 33.25, 17.5, 50, 4, 4}, /* ELM_A3 0.875 */
+ { 26.25, 14, 50, 4, 4}, /* ELM_A4 0.875 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"EE", 2,11, 7,0x69,"i","i",ELM_FEATURE_FNT|ELM_FEATURE_HGH|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 250, 110, 50, 4, 4}, /* ELM_F1 -15 */
+ { 2320, 1190, 50, 4, 4}, /* ELM_F2 30 */
+ { 3200, 1580, 50, 4, 4}, /* ELM_F3 -20 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 33.25, 17.5, 50, 4, 4}, /* ELM_A2 0.875 */
+ { 36.75, 17.5, 50, 4, 4}, /* ELM_A3 -0.875 */
+ { 31.5, 14, 50, 4, 4}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"ER", 2,16,16,0xCE,"3","V\"",ELM_FEATURE_CNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 580, 290, 50, 4, 4}, /* ELM_F1 0 */
+ { 1420, 710, 50, 4, 4}, /* ELM_F2 0 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 45.5, 21, 50, 4, 4}, /* ELM_A2 -1.75 */
+ { 33.25, 17.5, 50, 4, 4}, /* ELM_A3 0.875 */
+ { 26.25, 14, 50, 4, 4}, /* ELM_A4 0.875 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"AR", 2,15,15,0x41,"A","A",ELM_FEATURE_BCK|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 790, 410, 50, 4, 4}, /* ELM_F1 15 */
+ { 880, 470, 50, 4, 4}, /* ELM_F2 30 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 49, 24.5, 50, 4, 4}, /* ELM_A2 0 */
+ { 29.75, 14, 50, 4, 4}, /* ELM_A3 -0.875 */
+ { 22.75, 10.5, 50, 4, 4}, /* ELM_A4 -0.875 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"AW", 2,16,10,0x8D,"O","O",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_RND|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 230, 50, 4, 4}, /* ELM_F1 -15 */
+ { 820, 470, 50, 4, 4}, /* ELM_F2 60 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 45.5, 21, 50, 4, 4}, /* ELM_A2 -1.75 */
+ { 22.75, 10.5, 50, 4, 4}, /* ELM_A3 -0.875 */
+ { 17.5, 7, 50, 4, 4}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"UU", 2,14, 9,0x75,"u","u",ELM_FEATURE_BCK|ELM_FEATURE_HGH|ELM_FEATURE_RND|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 250, 110, 50, 4, 4}, /* ELM_F1 -15 */
+ { 880, 470, 50, 4, 4}, /* ELM_F2 30 */
+ { 2200, 1100, 50, 4, 4}, /* ELM_F3 0 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 38.5, 17.5, 50, 4, 4}, /* ELM_A2 -1.75 */
+ { 17.5, 7, 50, 4, 4}, /* ELM_A3 -1.75 */
+ { 10.5, 3.5, 50, 4, 4}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"AI", 2, 9, 6,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 640, 290, 50, 5, 5}, /* ELM_F1 -30 */
+ { 1600, 830, 50, 5, 5}, /* ELM_F2 30 */
+ { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 45.5, 21, 50, 5, 5}, /* ELM_A2 -1.75 */
+ { 35, 17.5, 50, 5, 5}, /* ELM_A3 0 */
+ { 29.75, 14, 50, 5, 5}, /* ELM_A4 -0.875 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"IE", 2, 9, 6,0x61,"a","a",ELM_FEATURE_CNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 790, 410, 50, 5, 5}, /* ELM_F1 15 */
+ { 880, 470, 50, 5, 5}, /* ELM_F2 30 */
+ { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 49, 24.5, 50, 5, 5}, /* ELM_A2 0 */
+ { 29.75, 14, 50, 5, 5}, /* ELM_A3 -0.875 */
+ { 22.75, 10.5, 50, 5, 5}, /* ELM_A4 -0.875 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"OI", 2, 9, 6,0x6F,"o","o",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_UMD|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 230, 50, 5, 5}, /* ELM_F1 -15 */
+ { 820, 350, 50, 5, 5}, /* ELM_F2 -60 */
+ { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 45.5, 21, 50, 5, 5}, /* ELM_A2 -1.75 */
+ { 22.75, 10.5, 50, 5, 5}, /* ELM_A3 -0.875 */
+ { 17.5, 7, 50, 5, 5}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"OU", 2, 9, 6,0x61,"a","a",ELM_FEATURE_CNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 790, 410, 50, 5, 5}, /* ELM_F1 15 */
+ { 1300, 590, 50, 5, 5}, /* ELM_F2 -60 */
+ { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 47.25, 24.5, 50, 5, 5}, /* ELM_A2 0.875 */
+ { 35, 17.5, 50, 5, 5}, /* ELM_A3 0 */
+ { 28, 14, 50, 5, 5}, /* ELM_A4 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"OV", 2, 8, 6,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 370, 170, 50, 4, 4}, /* ELM_F1 -15 */
+ { 1000, 470, 50, 4, 4}, /* ELM_F2 -30 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 42, 21, 50, 4, 4}, /* ELM_A2 0 */
+ { 28, 14, 50, 4, 4}, /* ELM_A3 0 */
+ { 22.75, 10.5, 50, 4, 4}, /* ELM_A4 -0.875 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"OA", 2, 9, 6,0xAB,"@","@",ELM_FEATURE_CNT|ELM_FEATURE_MDL|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 230, 50, 5, 5}, /* ELM_F1 -15 */
+ { 1480, 710, 50, 5, 5}, /* ELM_F2 -30 */
+ { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A2 -0.875 */
+ { 33.25, 17.5, 50, 5, 5}, /* ELM_A3 0.875 */
+ { 26.25, 14, 50, 5, 5}, /* ELM_A4 0.875 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"IA", 2, 9, 6,0x49,"I","I",ELM_FEATURE_FNT|ELM_FEATURE_SMH|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 310, 170, 50, 5, 5}, /* ELM_F1 15 */
+ { 2200, 1070, 50, 5, 5}, /* ELM_F2 -30 */
+ { 2920, 1460, 50, 5, 5}, /* ELM_F3 0 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 35, 17.5, 50, 5, 5}, /* ELM_A2 0 */
+ { 36.75, 17.5, 50, 5, 5}, /* ELM_A3 -0.875 */
+ { 31.5, 14, 50, 5, 5}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"IB", 2, 8, 6,0x51,"@","@",ELM_FEATURE_FNT|ELM_FEATURE_LOW|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 230, 50, 4, 4}, /* ELM_F1 -15 */
+ { 1480, 710, 50, 4, 4}, /* ELM_F2 -30 */
+ { 2500, 1220, 50, 4, 4}, /* ELM_F3 -30 */
+ { 60, 30, 50, 4, 4}, /* ELM_B1 0 */
+ { 90, 45, 50, 4, 4}, /* ELM_B2 0 */
+ { 150, 75, 50, 4, 4}, /* ELM_B3 0 */
+ { -30, 0, 100, 4, 4}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A1 -0.875 */
+ { 50.75, 24.5, 50, 4, 4}, /* ELM_A2 -0.875 */
+ { 33.25, 17.5, 50, 4, 4}, /* ELM_A3 0.875 */
+ { 26.25, 14, 50, 4, 4}, /* ELM_A4 0.875 */
+ { -30, -15, 50, 4, 4}, /* ELM_A5 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_A6 0 */
+ { -30, -15, 50, 4, 4}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"AIR", 2, 9, 6,0x45,"e","E",ELM_FEATURE_FNT|ELM_FEATURE_LMD|ELM_FEATURE_UNR|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 640, 350, 50, 5, 5}, /* ELM_F1 30 */
+ { 2020, 1070, 50, 5, 5}, /* ELM_F2 60 */
+ { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 42, 21, 50, 5, 5}, /* ELM_A2 0 */
+ { 38.5, 17.5, 50, 5, 5}, /* ELM_A3 -1.75 */
+ { 31.5, 14, 50, 5, 5}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"OOR", 2, 9, 6,0x55,"U","U",ELM_FEATURE_BCK|ELM_FEATURE_RND|ELM_FEATURE_SMH|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 370, 170, 50, 5, 5}, /* ELM_F1 -15 */
+ { 1000, 470, 50, 5, 5}, /* ELM_F2 -30 */
+ { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 42, 21, 50, 5, 5}, /* ELM_A2 0 */
+ { 28, 14, 50, 5, 5}, /* ELM_A3 0 */
+ { 22.75, 7, 50, 5, 5}, /* ELM_A4 -4.375 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+},
+
+{"OR", 2, 9, 6,0x8D,"O","O",ELM_FEATURE_BCK|ELM_FEATURE_LMD|ELM_FEATURE_RND|ELM_FEATURE_VWL,
+ {
+ { 270, 135, 50, 0, 0}, /* ELM_FN 0 */
+ { 490, 230, 50, 5, 5}, /* ELM_F1 -15 */
+ { 820, 470, 50, 5, 5}, /* ELM_F2 60 */
+ { 2500, 1220, 50, 5, 5}, /* ELM_F3 -30 */
+ { 60, 30, 50, 5, 5}, /* ELM_B1 0 */
+ { 90, 45, 50, 5, 5}, /* ELM_B2 0 */
+ { 150, 75, 50, 5, 5}, /* ELM_B3 0 */
+ { -30, 0, 100, 5, 5}, /* ELM_AN 0 */
+ { 50.75, 24.5, 50, 5, 5}, /* ELM_A1 -0.875 */
+ { 45.5, 21, 50, 5, 5}, /* ELM_A2 -1.75 */
+ { 22.75, 10.5, 50, 5, 5}, /* ELM_A3 -0.875 */
+ { 17.5, 7, 50, 5, 5}, /* ELM_A4 -1.75 */
+ { -30, -15, 50, 5, 5}, /* ELM_A5 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_A6 0 */
+ { -30, -15, 50, 5, 5}, /* ELM_AB 0 */
+ { 62, 31, 50, 0, 0}, /* ELM_AV 0 */
+ { 16, 8, 50, 0, 0}, /* ELM_AVC 0 */
+ { 0, 0, 50, 0, 0}, /* ELM_ASP 0 */
+ { 0, 0, 50, 0, 0} /* ELM_AF 0 */
+ }
+}
+
diff --git a/src/soloud/src/audiosource/speech/darray.cpp b/src/soloud/src/audiosource/speech/darray.cpp
new file mode 100644
index 0000000..4a6eae4
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/darray.cpp
@@ -0,0 +1,70 @@
+#include
+#include
+#include "darray.h"
+
+darray::darray()
+{
+ mAllocChunk = 128;
+ mAllocated = mUsed = 0;
+ mData = NULL;
+}
+
+void darray::clear()
+{
+ free(mData);
+ mAllocChunk = 128;
+ mAllocated = mUsed = 0;
+ mData = NULL;
+}
+
+darray::~darray()
+{
+ clear();
+}
+
+char * darray::getDataInPos(int aPosition)
+{
+ if (aPosition < mAllocated && aPosition < mUsed)
+ return mData + aPosition;
+
+ if (aPosition >= mAllocated)
+ {
+ int newsize = mAllocated;
+
+ while (newsize <= aPosition)
+ {
+ newsize += mAllocChunk;
+ mAllocChunk *= 2;
+ }
+
+ char *newdata = (char*)realloc(mData, newsize);
+ if (!newdata)
+ {
+ free(mData);
+ mData = NULL;
+ mAllocated = mUsed = 0;
+ return NULL;
+ }
+ else
+ {
+ memset(newdata + mAllocated, 0, newsize - mAllocated);
+ }
+
+ mData = newdata;
+ mAllocated = newsize;
+ }
+
+ if (aPosition >= mUsed)
+ {
+ mUsed = aPosition + 1;
+ }
+
+ return mData + aPosition;
+}
+
+void darray::put(int aData)
+{
+ char *s = getDataInPos(mUsed);
+
+ *s = aData;
+}
diff --git a/src/soloud/src/audiosource/speech/darray.h b/src/soloud/src/audiosource/speech/darray.h
new file mode 100644
index 0000000..d3251fb
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/darray.h
@@ -0,0 +1,22 @@
+#if !defined(DARRAY_H)
+#define DARRAY_H
+
+class darray
+{
+protected:
+ char *mData;
+ int mUsed;
+ int mAllocated;
+ int mAllocChunk;
+public:
+ darray();
+ ~darray();
+ void clear();
+ char *getDataInPos(int aPosition);
+ void put(int aData);
+ int getSize() const { return mUsed; }
+ char *getData() { return mData; }
+};
+
+#endif
+
diff --git a/src/soloud/src/audiosource/speech/klatt.cpp b/src/soloud/src/audiosource/speech/klatt.cpp
new file mode 100644
index 0000000..cd5e116
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/klatt.cpp
@@ -0,0 +1,1073 @@
+#include
+#include
+#include "klatt.h"
+#include "darray.h"
+#include "resonator.h"
+
+#ifndef PI
+#define PI 3.1415926535897932384626433832795f
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+class Interp
+{
+public:
+ float mSteady;
+ float mFixed;
+ char mProportion;
+ char mExtDelay;
+ char mIntDelay;
+};
+
+
+enum Eparm_e
+{
+ ELM_FN, ELM_F1, ELM_F2, ELM_F3,
+ ELM_B1, ELM_B2, ELM_B3, ELM_AN,
+ ELM_A1, ELM_A2, ELM_A3, ELM_A4,
+ ELM_A5, ELM_A6, ELM_AB, ELM_AV,
+ ELM_AVC, ELM_ASP, ELM_AF,
+ ELM_COUNT
+};
+
+class Element
+{
+public:
+ const char *mName; // unused
+ const char mRK;
+ const char mDU;
+ const char mUD;
+ unsigned char mFont; // unused
+ const char *mDict; // unused
+ const char *mIpa; // unused
+ int mFeat; // only ELM_FEATURE_VWL
+ Interp mInterpolator[ELM_COUNT];
+ };
+
+enum ELEMENT_FEATURES
+{
+ ELM_FEATURE_ALV = 0x00000001,
+ ELM_FEATURE_APR = 0x00000002,
+ ELM_FEATURE_BCK = 0x00000004,
+ ELM_FEATURE_BLB = 0x00000008,
+ ELM_FEATURE_CNT = 0x00000010,
+ ELM_FEATURE_DNT = 0x00000020,
+ ELM_FEATURE_FNT = 0x00000040,
+ ELM_FEATURE_FRC = 0x00000080,
+ ELM_FEATURE_GLT = 0x00000100,
+ ELM_FEATURE_HGH = 0x00000200,
+ ELM_FEATURE_LAT = 0x00000400,
+ ELM_FEATURE_LBD = 0x00000800,
+ ELM_FEATURE_LBV = 0x00001000,
+ ELM_FEATURE_LMD = 0x00002000,
+ ELM_FEATURE_LOW = 0x00004000,
+ ELM_FEATURE_MDL = 0x00008000,
+ ELM_FEATURE_NAS = 0x00010000,
+ ELM_FEATURE_PAL = 0x00020000,
+ ELM_FEATURE_PLA = 0x00040000,
+ ELM_FEATURE_RND = 0x00080000,
+ ELM_FEATURE_RZD = 0x00100000,
+ ELM_FEATURE_SMH = 0x00200000,
+ ELM_FEATURE_STP = 0x00400000,
+ ELM_FEATURE_UMD = 0x00800000,
+ ELM_FEATURE_UNR = 0x01000000,
+ ELM_FEATURE_VCD = 0x02000000,
+ ELM_FEATURE_VEL = 0x04000000,
+ ELM_FEATURE_VLS = 0x08000000,
+ ELM_FEATURE_VWL = 0x10000000
+};
+
+enum ELEMENTS
+{
+ ELM_END = 0,
+ ELM_Q, ELM_P, ELM_PY, ELM_PZ, ELM_T, ELM_TY,
+ ELM_TZ, ELM_K, ELM_KY, ELM_KZ, ELM_B, ELM_BY, ELM_BZ,
+ ELM_D, ELM_DY, ELM_DZ, ELM_G, ELM_GY, ELM_GZ, ELM_M,
+ ELM_N, ELM_NG, ELM_F, ELM_TH, ELM_S, ELM_SH, ELM_X,
+ ELM_H, ELM_V, ELM_QQ, ELM_DH, ELM_DI, ELM_Z, ELM_ZZ,
+ ELM_ZH, ELM_CH, ELM_CI, ELM_J, ELM_JY, ELM_L, ELM_LL,
+ ELM_RX, ELM_R, ELM_W, ELM_Y, ELM_I, ELM_E, ELM_AA,
+ ELM_U, ELM_O, ELM_OO, ELM_A, ELM_EE, ELM_ER, ELM_AR,
+ ELM_AW, ELM_UU, ELM_AI, ELM_IE, ELM_OI, ELM_OU, ELM_OV,
+ ELM_OA, ELM_IA, ELM_IB, ELM_AIR,ELM_OOR,ELM_OR
+};
+
+#define PHONEME_COUNT 53
+#define AMP_ADJ 14
+#define StressDur(e,s) (s,((e->mDU + e->mUD)/2))
+
+
+
+
+class PhonemeToElements
+{
+public:
+ int mKey;
+ char mData[8];
+};
+
+/* Order is important - 2 byte phonemes first, otherwise
+ the search function will fail*/
+static PhonemeToElements phoneme_to_elements[PHONEME_COUNT] =
+{
+ /* mKey, count, 0-7 elements */
+/* tS */ 0x5374, 2, ELM_CH, ELM_CI, 0, 0, 0, 0, 0,
+/* dZ */ 0x5a64, 4, ELM_J, ELM_JY, ELM_QQ, ELM_JY, 0, 0, 0,
+/* rr */ 0x7272, 3, ELM_R, ELM_QQ, ELM_R, 0, 0, 0, 0,
+/* eI */ 0x4965, 2, ELM_AI, ELM_I, 0, 0, 0, 0, 0,
+/* aI */ 0x4961, 2, ELM_IE, ELM_I, 0, 0, 0, 0, 0,
+/* oI */ 0x496f, 2, ELM_OI, ELM_I, 0, 0, 0, 0, 0,
+/* aU */ 0x5561, 2, ELM_OU, ELM_OV, 0, 0, 0, 0, 0,
+/* @U */ 0x5540, 2, ELM_OA, ELM_OV, 0, 0, 0, 0, 0,
+/* I@ */ 0x4049, 2, ELM_IA, ELM_IB, 0, 0, 0, 0, 0,
+/* e@ */ 0x4065, 2, ELM_AIR, ELM_IB, 0, 0, 0, 0, 0,
+/* U@ */ 0x4055, 2, ELM_OOR, ELM_IB, 0, 0, 0, 0, 0,
+/* O@ */ 0x404f, 2, ELM_OR, ELM_IB, 0, 0, 0, 0, 0,
+/* oU */ 0x556f, 2, ELM_OI, ELM_OV, 0, 0, 0, 0, 0,
+/* */ 0x0020, 1, ELM_Q, 0, 0, 0, 0, 0, 0,
+/* p */ 0x0070, 3, ELM_P, ELM_PY, ELM_PZ, 0, 0, 0, 0,
+/* t */ 0x0074, 3, ELM_T, ELM_TY, ELM_TZ, 0, 0, 0, 0,
+/* k */ 0x006b, 3, ELM_K, ELM_KY, ELM_KZ, 0, 0, 0, 0,
+/* b */ 0x0062, 3, ELM_B, ELM_BY, ELM_BZ, 0, 0, 0, 0,
+/* d */ 0x0064, 3, ELM_D, ELM_DY, ELM_DZ, 0, 0, 0, 0,
+/* g */ 0x0067, 3, ELM_G, ELM_GY, ELM_GZ, 0, 0, 0, 0,
+/* m */ 0x006d, 1, ELM_M, 0, 0, 0, 0, 0, 0,
+/* n */ 0x006e, 1, ELM_N, 0, 0, 0, 0, 0, 0,
+/* N */ 0x004e, 1, ELM_NG, 0, 0, 0, 0, 0, 0,
+/* f */ 0x0066, 1, ELM_F, 0, 0, 0, 0, 0, 0,
+/* T */ 0x0054, 1, ELM_TH, 0, 0, 0, 0, 0, 0,
+/* s */ 0x0073, 1, ELM_S, 0, 0, 0, 0, 0, 0,
+/* S */ 0x0053, 1, ELM_SH, 0, 0, 0, 0, 0, 0,
+/* h */ 0x0068, 1, ELM_H, 0, 0, 0, 0, 0, 0,
+/* v */ 0x0076, 3, ELM_V, ELM_QQ, ELM_V, 0, 0, 0, 0,
+/* D */ 0x0044, 3, ELM_DH, ELM_QQ, ELM_DI, 0, 0, 0, 0,
+/* z */ 0x007a, 3, ELM_Z, ELM_QQ, ELM_ZZ, 0, 0, 0, 0,
+/* Z */ 0x005a, 3, ELM_ZH, ELM_QQ, ELM_ZH, 0, 0, 0, 0,
+/* l */ 0x006c, 1, ELM_L, 0, 0, 0, 0, 0, 0,
+/* r */ 0x0072, 1, ELM_R, 0, 0, 0, 0, 0, 0,
+/* R */ 0x0052, 1, ELM_RX, 0, 0, 0, 0, 0, 0,
+/* w */ 0x0077, 1, ELM_W, 0, 0, 0, 0, 0, 0,
+/* x */ 0x0078, 1, ELM_X, 0, 0, 0, 0, 0, 0,
+/* % */ 0x0025, 1, ELM_QQ, 0, 0, 0, 0, 0, 0,
+/* j */ 0x006a, 1, ELM_Y, 0, 0, 0, 0, 0, 0,
+/* I */ 0x0049, 1, ELM_I, 0, 0, 0, 0, 0, 0,
+/* e */ 0x0065, 1, ELM_E, 0, 0, 0, 0, 0, 0,
+/* & */ 0x0026, 1, ELM_AA, 0, 0, 0, 0, 0, 0,
+/* V */ 0x0056, 1, ELM_U, 0, 0, 0, 0, 0, 0,
+/* 0 */ 0x0030, 1, ELM_O, 0, 0, 0, 0, 0, 0,
+/* U */ 0x0055, 1, ELM_OO, 0, 0, 0, 0, 0, 0,
+/* @ */ 0x0040, 1, ELM_A, 0, 0, 0, 0, 0, 0,
+/* i */ 0x0069, 1, ELM_EE, 0, 0, 0, 0, 0, 0,
+/* 3 */ 0x0033, 1, ELM_ER, 0, 0, 0, 0, 0, 0,
+/* A */ 0x0041, 1, ELM_AR, 0, 0, 0, 0, 0, 0,
+/* O */ 0x004f, 1, ELM_AW, 0, 0, 0, 0, 0, 0,
+/* u */ 0x0075, 1, ELM_UU, 0, 0, 0, 0, 0, 0,
+/* o */ 0x006f, 1, ELM_OI, 0, 0, 0, 0, 0, 0,
+/* . */ 0x002e, 1, ELM_END,0, 0, 0, 0, 0, 0,
+};
+
+static Element gElement[] =
+{
+#include "Elements.def"
+};
+
+static short clip(float input)
+{
+ int temp = (int)input;
+ /* clip on boundaries of 16-bit word */
+
+ if (temp < -32767)
+ {
+ //assert?
+ temp = -32767;
+ }
+ else
+ if (temp > 32767)
+ {
+ //assert?
+ temp = 32767;
+ }
+
+ return (short)(temp);
+}
+
+/* Convert from decibels to a linear scale factor */
+static float DBtoLIN(int dB)
+{
+ /*
+ * Convertion table, db to linear, 87 dB --> 32767
+ * 86 dB --> 29491 (1 dB down = 0.5**1/6)
+ * ...
+ * 81 dB --> 16384 (6 dB down = 0.5)
+ * ...
+ * 0 dB --> 0
+ *
+ * The just noticeable difference for a change in intensity of a vowel
+ * is approximately 1 dB. Thus all amplitudes are quantized to 1 dB
+ * steps.
+ */
+
+ static const float amptable[88] =
+ {
+ 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 6.0, 7.0,
+ 8.0, 9.0, 10.0, 11.0, 13.0,
+ 14.0, 16.0, 18.0, 20.0, 22.0,
+ 25.0, 28.0, 32.0, 35.0, 40.0,
+ 45.0, 51.0, 57.0, 64.0, 71.0,
+ 80.0, 90.0, 101.0, 114.0, 128.0,
+ 142.0, 159.0, 179.0, 202.0, 227.0,
+ 256.0, 284.0, 318.0, 359.0, 405.0,
+ 455.0, 512.0, 568.0, 638.0, 719.0,
+ 811.0, 911.0, 1024.0, 1137.0, 1276.0,
+ 1438.0, 1622.0, 1823.0, 2048.0, 2273.0,
+ 2552.0, 2875.0, 3244.0, 3645.0, 4096.0,
+ 4547.0, 5104.0, 5751.0, 6488.0, 7291.0,
+ 8192.0, 9093.0, 10207.0, 11502.0, 12976.0,
+ 14582.0, 16384.0, 18350.0, 20644.0, 23429.0,
+ 26214.0, 29491.0, 32767.0
+ };
+
+ // Check limits or argument (can be removed in final product)
+ if (dB < 0)
+ {
+ dB = 0;
+ }
+ else
+ if (dB >= 88)
+ {
+ dB = 87;
+ }
+
+ return amptable[dB] * 0.001f;
+}
+
+
+
+klatt_frame::klatt_frame() :
+ mF0FundamentalFreq(1330), mVoicingAmpdb(60), mFormant1Freq(500),
+ mFormant1Bandwidth(60), mFormant2Freq(1500), mFormant2Bandwidth(90),
+ mFormant3Freq(2800), mFormant3Bandwidth(150), mFormant4Freq(3250),
+ mFormant4Bandwidth(200), mFormant5Freq(3700), mFormant5Bandwidth(200),
+ mFormant6Freq(4990), mFormant6Bandwidth(500), mNasalZeroFreq(270),
+ mNasalZeroBandwidth(100), mNasalPoleFreq(270), mNasalPoleBandwidth(100),
+ mAspirationAmpdb(0), mNoSamplesInOpenPeriod(30), mVoicingBreathiness(0),
+ mVoicingSpectralTiltdb(10), mFricationAmpdb(0), mSkewnessOfAlternatePeriods(0),
+ mFormant1Ampdb(0), mFormant1ParallelBandwidth(80), mFormant2Ampdb(0),
+ mFormant2ParallelBandwidth(200), mFormant3Ampdb(0), mFormant3ParallelBandwidth(350),
+ mFormant4Ampdb(0), mFormant4ParallelBandwidth(500), mFormant5Ampdb(0),
+ mFormant5ParallelBandwidth(600), mFormant6Ampdb(0), mFormant6ParallelBandwidth(800),
+ mParallelNasalPoleAmpdb(0), mBypassFricationAmpdb(0), mPalallelVoicingAmpdb(0),
+ mOverallGaindb(62)
+{
+};
+
+
+klatt::klatt() :
+ mBaseF0(1330),
+ mBaseSpeed(10.0f),
+ mBaseDeclination(0.5f),
+ mBaseWaveform(KW_SAW),
+ mF0Flutter(0),
+ mSampleRate(0),
+ mNspFr(0),
+ mF0FundamentalFreq(0),
+ mVoicingAmpdb(0),
+ mSkewnessOfAlternatePeriods(0),
+ mTimeCount(0),
+ mNPer(0),
+ mT0(0),
+ mNOpen(0),
+ mNMod(0),
+ mAmpVoice(0),
+ mAmpBypas(0),
+ mAmpAspir(0),
+ mAmpFrica(0),
+ mAmpBreth(0),
+ mSkew(0),
+ mVLast(0),
+ mNLast(0),
+ mGlotLast(0),
+ mDecay(0),
+ mOneMd(0),
+ mElementCount(0),
+ mElement(0),
+ mElementIndex(0),
+ mLastElement(0),
+ mTStress(0),
+ mNTStress(0),
+ mTop(0)
+{
+}
+
+/*
+function FLUTTER
+
+This function adds F0 flutter, as specified in:
+
+"Analysis, synthesis and perception of voice quality variations among
+female and male talkers" D.H. Klatt and L.C. Klatt JASA 87(2) February 1990.
+Flutter is added by applying a quasi-random element constructed from three
+slowly varying sine waves.
+*/
+void klatt::flutter()
+{
+ int original_f0 = mFrame.mF0FundamentalFreq / 10;
+ float fla = (float) mF0Flutter / 50;
+ float flb = (float) original_f0 / 100;
+ float flc = (float)sin(2 * PI * 12.7 * mTimeCount);
+ float fld = (float)sin(2 * PI * 7.1 * mTimeCount);
+ float fle = (float)sin(2 * PI * 4.7 * mTimeCount);
+ float delta_f0 = fla * flb * (flc + fld + fle) * 10;
+ mF0FundamentalFreq += (int) delta_f0;
+}
+
+/* Vwave is the differentiated glottal flow waveform, there is a weak
+spectral zero around 800 Hz, magic constants a,b reset pitch-synch
+*/
+
+float klatt::natural_source(int aNper)
+{
+ // See if glottis open
+ if (aNper < mNOpen)
+ {
+ switch (mBaseWaveform)
+ {
+ case KW_TRIANGLE:
+ return ((aNper % 200) - 100) * 81.92f; // triangle
+ case KW_SIN:
+ return (float)(sin(aNper * 0.0314) * 8192); // sin
+ case KW_SQUARE:
+ return ((aNper % 200) - 100) > 0 ? 8192.0f : -8192.0f; // square
+ case KW_PULSE:
+ return ((aNper % 200) - 100) > 50 ? 8192.0f : -8192.0f; // pulse
+ case KW_NOISE:
+ return (int)mNLast & 1 ? -8192.0f : 8192.0f;
+ case KW_WARBLE:
+ return (int)mNLast & 7 ? -8192.0f : 8192.0f;
+ case KW_SAW: // fallthrough
+ default:
+ return (abs((aNper % 200) - 100) - 50) * 163.84f; // saw
+ }
+ }
+ else
+ {
+ // Glottis closed
+ return (0.0);
+ }
+
+}
+
+/* Reset selected parameters pitch-synchronously */
+
+void klatt::pitch_synch_par_reset(int ns)
+{
+ if (mF0FundamentalFreq > 0)
+ {
+ mT0 = (40 * mSampleRate) / mF0FundamentalFreq;
+
+ /* Period in samp*4 */
+ mAmpVoice = DBtoLIN(mVoicingAmpdb);
+
+ /* Duration of period before amplitude modulation */
+ mNMod = mT0;
+
+ if (mVoicingAmpdb > 0)
+ {
+ mNMod >>= 1;
+ }
+
+ /* Breathiness of voicing waveform */
+
+ mAmpBreth = DBtoLIN(mFrame.mVoicingBreathiness) * 0.1f;
+
+ /* Set open phase of glottal period */
+ /* where 40 <= open phase <= 263 */
+
+ mNOpen = 4 * mFrame.mNoSamplesInOpenPeriod;
+
+ if (mNOpen >= (mT0 - 1))
+ {
+ mNOpen = mT0 - 2;
+ }
+
+ if (mNOpen < 40)
+ {
+ mNOpen = 40; /* F0 max = 1000 Hz */
+ }
+
+ int temp;
+ float temp1;
+
+ temp = mSampleRate / mNOpen;
+ mCritDampedGlotLowPassFilter.initResonator(0L, temp, mSampleRate);
+
+ /* Make gain at F1 about constant */
+
+ temp1 = mNOpen * .00833f;
+ mCritDampedGlotLowPassFilter.setGain(temp1 * temp1);
+
+ /* Truncate skewness so as not to exceed duration of closed phase
+ of glottal period */
+
+ temp = mT0 - mNOpen;
+
+ if (mSkewnessOfAlternatePeriods > temp)
+ {
+ mSkewnessOfAlternatePeriods = temp;
+ }
+
+ if (mSkew >= 0)
+ {
+ mSkew = mSkewnessOfAlternatePeriods; /* Reset mSkew to requested mSkewnessOfAlternatePeriods */
+ }
+ else
+ {
+ mSkew = -mSkewnessOfAlternatePeriods;
+ }
+
+ /* Add skewness to closed portion of voicing period */
+
+ mT0 = mT0 + mSkew;
+ mSkew = -mSkew;
+ }
+ else
+ {
+ mT0 = 4; /* Default for f0 undefined */
+ mAmpVoice = 0.0;
+ mNMod = mT0;
+ mAmpBreth = 0.0;
+ }
+
+ /* Reset these pars pitch synchronously or at update rate if f0=0 */
+
+ if ((mT0 != 4) || (ns == 0))
+ {
+ /* Set one-pole ELM_FEATURE_LOW-pass filter that tilts glottal source */
+ mDecay = (0.033f * mFrame.mVoicingSpectralTiltdb); /* Function of samp_rate ? */
+
+ if (mDecay > 0.0f)
+ {
+ mOneMd = 1.0f - mDecay;
+ }
+ else
+ {
+ mOneMd = 1.0f;
+ }
+ }
+}
+
+
+/* Get variable parameters from host computer,
+initially also get definition of fixed pars
+*/
+
+void klatt::frame_init()
+{
+ int mOverallGaindb; /* Overall gain, 60 dB is unity 0 to 60 */
+ float amp_parF1; /* mFormant1Ampdb converted to linear gain */
+ float amp_parFN; /* mParallelNasalPoleAmpdb converted to linear gain */
+ float amp_parF2; /* mFormant2Ampdb converted to linear gain */
+ float amp_parF3; /* mFormant3Ampdb converted to linear gain */
+ float amp_parF4; /* mFormant4Ampdb converted to linear gain */
+ float amp_parF5; /* mFormant5Ampdb converted to linear gain */
+ float amp_parF6; /* mFormant6Ampdb converted to linear gain */
+
+ /* Read speech frame definition into temp store
+ and move some parameters into active use immediately
+ (voice-excited ones are updated pitch synchronously
+ to avoid waveform glitches).
+ */
+
+ mF0FundamentalFreq = mFrame.mF0FundamentalFreq;
+ mVoicingAmpdb = mFrame.mVoicingAmpdb - 7;
+
+ if (mVoicingAmpdb < 0) mVoicingAmpdb = 0;
+
+ mAmpAspir = DBtoLIN(mFrame.mAspirationAmpdb) * .05f;
+ mAmpFrica = DBtoLIN(mFrame.mFricationAmpdb) * 0.25f;
+ mSkewnessOfAlternatePeriods = mFrame.mSkewnessOfAlternatePeriods;
+
+ /* Fudge factors (which comprehend affects of formants on each other?)
+ with these in place ALL_PARALLEL should sound as close as
+ possible to CASCADE_PARALLEL.
+ Possible problem feeding in Holmes's amplitudes given this.
+ */
+ amp_parF1 = DBtoLIN(mFrame.mFormant1Ampdb) * 0.4f; /* -7.96 dB */
+ amp_parF2 = DBtoLIN(mFrame.mFormant2Ampdb) * 0.15f; /* -16.5 dB */
+ amp_parF3 = DBtoLIN(mFrame.mFormant3Ampdb) * 0.06f; /* -24.4 dB */
+ amp_parF4 = DBtoLIN(mFrame.mFormant4Ampdb) * 0.04f; /* -28.0 dB */
+ amp_parF5 = DBtoLIN(mFrame.mFormant5Ampdb) * 0.022f; /* -33.2 dB */
+ amp_parF6 = DBtoLIN(mFrame.mFormant6Ampdb) * 0.03f; /* -30.5 dB */
+ amp_parFN = DBtoLIN(mFrame.mParallelNasalPoleAmpdb) * 0.6f; /* -4.44 dB */
+ mAmpBypas = DBtoLIN(mFrame.mBypassFricationAmpdb) * 0.05f; /* -26.0 db */
+
+ // Set coeficients of nasal resonator and zero antiresonator
+ mNasalPole.initResonator(mFrame.mNasalPoleFreq, mFrame.mNasalPoleBandwidth, mSampleRate);
+
+ mNasalZero.initAntiresonator(mFrame.mNasalZeroFreq, mFrame.mNasalZeroBandwidth, mSampleRate);
+
+ // Set coefficients of parallel resonators, and amplitude of outputs
+ mParallelFormant1.initResonator(mFrame.mFormant1Freq, mFrame.mFormant1ParallelBandwidth, mSampleRate);
+ mParallelFormant1.setGain(amp_parF1);
+
+ mParallelResoNasalPole.initResonator(mFrame.mNasalPoleFreq, mFrame.mNasalPoleBandwidth, mSampleRate);
+ mParallelResoNasalPole.setGain(amp_parFN);
+
+ mParallelFormant2.initResonator(mFrame.mFormant2Freq, mFrame.mFormant2ParallelBandwidth, mSampleRate);
+ mParallelFormant2.setGain(amp_parF2);
+
+ mParallelFormant3.initResonator(mFrame.mFormant3Freq, mFrame.mFormant3ParallelBandwidth, mSampleRate);
+ mParallelFormant3.setGain(amp_parF3);
+
+ mParallelFormant4.initResonator(mFrame.mFormant4Freq, mFrame.mFormant4ParallelBandwidth, mSampleRate);
+ mParallelFormant4.setGain(amp_parF4);
+
+ mParallelFormant5.initResonator(mFrame.mFormant5Freq, mFrame.mFormant5ParallelBandwidth, mSampleRate);
+ mParallelFormant5.setGain(amp_parF5);
+
+ mParallelFormant6.initResonator(mFrame.mFormant6Freq, mFrame.mFormant6ParallelBandwidth, mSampleRate);
+ mParallelFormant6.setGain(amp_parF6);
+
+
+ /* fold overall gain into output resonator */
+ mOverallGaindb = mFrame.mOverallGaindb - 3;
+
+ if (mOverallGaindb <= 0)
+ mOverallGaindb = 57;
+
+ /* output ELM_FEATURE_LOW-pass filter - resonator with freq 0 and BW = globals->mSampleRate
+ Thus 3db point is globals->mSampleRate/2 i.e. Nyquist limit.
+ Only 3db down seems rather mild...
+ */
+ mOutputLowPassFilter.initResonator(0L, (int)mSampleRate, mSampleRate);
+ mOutputLowPassFilter.setGain(DBtoLIN(mOverallGaindb));
+}
+
+/*
+function PARWAV
+
+CONVERT FRAME OF PARAMETER DATA TO A WAVEFORM CHUNK
+Synthesize globals->mNspFr samples of waveform and store in jwave[].
+*/
+
+void klatt::parwave(short int *jwave)
+{
+ /* Output of cascade branch, also final output */
+
+ /* Initialize synthesizer and get specification for current speech
+ frame from host microcomputer */
+
+ frame_init();
+
+ if (mF0Flutter != 0)
+ {
+ mTimeCount++; /* used for f0 flutter */
+ flutter(); /* add f0 flutter */
+ }
+
+ /* MAIN LOOP, for each output sample of current frame: */
+
+ int ns;
+ for (ns = 0; ns < mNspFr; ns++)
+ {
+ float noise;
+ int n4;
+ float sourc; /* Sound source if all-parallel config used */
+ float glotout; /* Output of glottal sound source */
+ float par_glotout; /* Output of parallelglottal sound sourc */
+ float voice = 0; /* Current sample of voicing waveform */
+ float frics; /* Frication sound source */
+ float aspiration; /* Aspiration sound source */
+ int nrand; /* Varible used by random number generator */
+
+ /* Our own code like rand(), but portable
+ whole upper 31 bits of seed random
+ assumes 32-bit unsigned arithmetic
+ with untested code to handle larger.
+ */
+ mSeed = mSeed * 1664525 + 1;
+
+ mSeed &= 0xFFFFFFFF;
+
+ /* Shift top bits of seed up to top of int then back down to LS 14 bits */
+ /* Assumes 8 bits per sizeof unit i.e. a "byte" */
+ nrand = (((int) mSeed) << (8 * sizeof(int) - 32)) >> (8 * sizeof(int) - 14);
+
+ /* Tilt down noise spectrum by soft ELM_FEATURE_LOW-pass filter having
+ * a pole near the origin in the z-plane, i.e.
+ * output = input + (0.75 * lastoutput) */
+
+ noise = nrand + (0.75f * mNLast); /* Function of samp_rate ? */
+
+ mNLast = noise;
+
+ /* Amplitude modulate noise (reduce noise amplitude during
+ second half of glottal period) if voicing simultaneously present
+ */
+
+ if (mNPer > mNMod)
+ {
+ noise *= 0.5f;
+ }
+
+ /* Compute frication noise */
+ sourc = frics = mAmpFrica * noise;
+
+ /* Compute voicing waveform : (run glottal source simulation at
+ 4 times normal sample rate to minimize quantization noise in
+ period of female voice)
+ */
+
+ for (n4 = 0; n4 < 4; n4++)
+ {
+ /* use a more-natural-shaped source waveform with excitation
+ occurring both upon opening and upon closure, stronest at closure */
+ voice = natural_source(mNPer);
+
+ /* Reset period when counter 'mNPer' reaches mT0 */
+
+ if (mNPer >= mT0)
+ {
+ mNPer = 0;
+ pitch_synch_par_reset(ns);
+ }
+
+ /* Low-pass filter voicing waveform before downsampling from 4*globals->mSampleRate */
+ /* to globals->mSampleRate samples/sec. Resonator f=.09*globals->mSampleRate, bw=.06*globals->mSampleRate */
+
+ voice = mDownSampLowPassFilter.resonate(voice); /* in=voice, out=voice */
+
+ /* Increment counter that keeps track of 4*globals->mSampleRate samples/sec */
+ mNPer++;
+ }
+
+ /* Tilt spectrum of voicing source down by soft ELM_FEATURE_LOW-pass filtering, amount
+ of tilt determined by mVoicingSpectralTiltdb
+ */
+ voice = (voice * mOneMd) + (mVLast * mDecay);
+
+ mVLast = voice;
+
+ /* Add breathiness during glottal open phase */
+ if (mNPer < mNOpen)
+ {
+ /* Amount of breathiness determined by parameter mVoicingBreathiness */
+ /* Use nrand rather than noise because noise is ELM_FEATURE_LOW-passed */
+ voice += mAmpBreth * nrand;
+ }
+
+ /* Set amplitude of voicing */
+ glotout = mAmpVoice * voice;
+
+ /* Compute aspiration amplitude and add to voicing source */
+ aspiration = mAmpAspir * noise;
+
+ glotout += aspiration;
+
+ par_glotout = glotout;
+
+ /* NIS - rsynth "hack"
+ As Holmes' scheme is weak at nasals and (physically) nasal cavity
+ is "back near glottis" feed glottal source through nasal resonators
+ Don't think this is quite right, but improves things a bit
+ */
+ par_glotout = mNasalZero.antiresonate(par_glotout);
+ par_glotout = mNasalPole.resonate(par_glotout);
+ /* And just use mParallelFormant1 NOT mParallelResoNasalPole */
+ float out = mParallelFormant1.resonate(par_glotout);
+ /* Sound sourc for other parallel resonators is frication
+ plus first difference of voicing waveform.
+ */
+ sourc += (par_glotout - mGlotLast);
+ mGlotLast = par_glotout;
+
+ /* Standard parallel vocal tract
+ Formants F6,F5,F4,F3,F2, outputs added with alternating sign
+ */
+ out = mParallelFormant6.resonate(sourc) - out;
+ out = mParallelFormant5.resonate(sourc) - out;
+ out = mParallelFormant4.resonate(sourc) - out;
+ out = mParallelFormant3.resonate(sourc) - out;
+ out = mParallelFormant2.resonate(sourc) - out;
+
+ out = mAmpBypas * sourc - out;
+ out = mOutputLowPassFilter.resonate(out);
+
+ *jwave++ = clip(out); /* Convert back to integer */
+ }
+}
+
+
+
+static char * phoneme_to_element_lookup(char *s, void ** data)
+{
+ int key8 = *s;
+ int key16 = key8 + (s[1] << 8);
+ if (s[1] == 0) key16 = -1; // avoid key8==key16
+ int i;
+ for (i = 0; i < PHONEME_COUNT; i++)
+ {
+ if (phoneme_to_elements[i].mKey == key16)
+ {
+ *data = &phoneme_to_elements[i].mData;
+ return s+2;
+ }
+ if (phoneme_to_elements[i].mKey == key8)
+ {
+ *data = &phoneme_to_elements[i].mData;
+ return s+1;
+ }
+ }
+ // should never happen
+ *data = NULL;
+ return s+1;
+}
+
+
+
+int klatt::phone_to_elm(char *aPhoneme, int aCount, darray *aElement)
+{
+ int stress = 0;
+ char *s = aPhoneme;
+ int t = 0;
+ char *limit = s + aCount;
+
+ while (s < limit && *s)
+ {
+ char *e = NULL;
+ s = phoneme_to_element_lookup(s, (void**)&e);
+
+ if (e)
+ {
+ int n = *e++;
+
+ while (n-- > 0)
+ {
+ int x = *e++;
+ Element * p = &gElement[x];
+ /* This works because only vowels have mUD != mDU,
+ and we set stress just before a vowel
+ */
+ aElement->put(x);
+
+ if (!(p->mFeat & ELM_FEATURE_VWL))
+ stress = 0;
+
+ int stressdur = StressDur(p,stress);
+
+ t += stressdur;
+
+ aElement->put(stressdur);
+ aElement->put(stress);
+ }
+ }
+
+ else
+ {
+ char ch = *s++;
+
+ switch (ch)
+ {
+
+ case '\'': /* Primary stress */
+ stress = 3;
+ break;
+
+ case ',': /* Secondary stress */
+ stress = 2;
+ break;
+
+ case '+': /* Tertiary stress */
+ stress = 1;
+ break;
+
+ case '-': /* hyphen in input */
+ break;
+
+ default:
+// fprintf(stderr, "Ignoring %c in '%.*s'\n", ch, aCount, aPhoneme);
+ break;
+ }
+ }
+ }
+
+ return t;
+}
+
+
+
+/* 'a' is dominant element, 'b' is dominated
+ ext is flag to say to use external times from 'a' rather
+ than internal i.e. ext != 0 if 'a' is NOT current element.
+ */
+
+static void set_trans(Slope *t, Element * a, Element * b,int ext, int e)
+{
+ int i;
+
+ for (i = 0; i < ELM_COUNT; i++)
+ {
+ t[i].mTime = ((ext) ? a->mInterpolator[i].mExtDelay : a->mInterpolator[i].mIntDelay);
+
+ if (t[i].mTime)
+ {
+ t[i].mValue = a->mInterpolator[i].mFixed + (a->mInterpolator[i].mProportion * b->mInterpolator[i].mSteady) * 0.01f; // mProportion is in scale 0..100, so *0.01.
+ }
+ else
+ {
+ t[i].mValue = b->mInterpolator[i].mSteady;
+ }
+ }
+}
+
+static float lerp(float a, float b, int t, int d)
+{
+ if (t <= 0)
+ {
+ return a;
+ }
+
+ if (t >= d)
+ {
+ return b;
+ }
+
+ float f = (float)t / (float)d;
+ return a + (b - a) * f;
+}
+
+static float interpolate(Slope *aStartSlope, Slope *aEndSlope, float aMidValue, int aTime, int aDuration)
+{
+ int steadyTime = aDuration - (aStartSlope->mTime + aEndSlope->mTime);
+
+ if (steadyTime >= 0)
+ {
+ // Interpolate to a midpoint, stay there for a while, then interpolate to end
+
+ if (aTime < aStartSlope->mTime)
+ {
+ // interpolate to the first value
+ return lerp(aStartSlope->mValue, aMidValue, aTime, aStartSlope->mTime);
+ }
+ // reached midpoint
+
+ aTime -= aStartSlope->mTime;
+
+ if (aTime <= steadyTime)
+ {
+ // still at steady state
+ return aMidValue;
+ }
+
+ // interpolate to the end
+ return lerp(aMidValue, aEndSlope->mValue, aTime - steadyTime, aEndSlope->mTime);
+ }
+ else
+ {
+ // No steady state
+ float f = 1.0f - ((float) aTime / (float) aDuration);
+ float sp = lerp(aStartSlope->mValue, aMidValue, aTime, aStartSlope->mTime);
+ float ep = lerp(aEndSlope->mValue, aMidValue, aDuration - aTime, aEndSlope->mTime);
+ return f * sp + ((float) 1.0 - f) * ep;
+ }
+}
+
+
+
+void klatt::initsynth(int aElementCount,unsigned char *aElement)
+{
+ mElement = aElement;
+ mElementCount = aElementCount;
+ mElementIndex = 0;
+ mLastElement = &gElement[0];
+ mSeed = 5;
+ mTStress = 0;
+ mNTStress = 0;
+ mFrame.mF0FundamentalFreq = mBaseF0;
+ mTop = 1.1f * mFrame.mF0FundamentalFreq;
+ mFrame.mNasalPoleFreq = (int)mLastElement->mInterpolator[ELM_FN].mSteady;
+ mFrame.mFormant1ParallelBandwidth = mFrame.mFormant1Bandwidth = 60;
+ mFrame.mFormant2ParallelBandwidth = mFrame.mFormant2Bandwidth = 90;
+ mFrame.mFormant3ParallelBandwidth = mFrame.mFormant3Bandwidth = 150;
+// mFrame.mFormant4ParallelBandwidth = (default)
+
+ // Set stress attack/decay slope
+ mStressS.mTime = 40;
+ mStressE.mTime = 40;
+ mStressE.mValue = 0.0;
+}
+
+int klatt::synth(int aSampleCount, short *aSamplePointer)
+{
+ short *samp = aSamplePointer;
+
+ if (mElementIndex >= mElementCount)
+ return -1;
+
+ Element * currentElement = &gElement[mElement[mElementIndex++]];
+ int dur = mElement[mElementIndex++];
+ mElementIndex++; // skip stress
+
+ if (currentElement->mRK == 31) // "END"
+ {
+ // Reset the fundamental frequency top
+ mFrame.mF0FundamentalFreq = mBaseF0;
+ mTop = 1.1f * mFrame.mF0FundamentalFreq;
+ }
+
+ // Skip zero length elements which are only there to affect
+ // boundary values of adjacent elements
+
+ if (dur > 0)
+ {
+ Element * ne = (mElementIndex < mElementCount) ? &gElement[mElement[mElementIndex]] : &gElement[0];
+ Slope start[ELM_COUNT];
+ Slope end[ELM_COUNT];
+ int t;
+
+ if (currentElement->mRK > mLastElement->mRK)
+ {
+ set_trans(start, currentElement, mLastElement, 0, 's');
+ // we dominate last
+ }
+ else
+ {
+ set_trans(start, mLastElement, currentElement, 1, 's');
+ // last dominates us
+ }
+
+ if (ne->mRK > currentElement->mRK)
+ {
+ set_trans(end, ne, currentElement, 1, 'e');
+ // next dominates us
+ }
+ else
+ {
+ set_trans(end, currentElement, ne, 0, 'e');
+ // we dominate next
+ }
+
+ for (t = 0; t < dur; t++, mTStress++)
+ {
+ float base = mTop * 0.8f; // 3 * top / 5
+ float tp[ELM_COUNT];
+ int j;
+
+ if (mTStress == mNTStress)
+ {
+ int j = mElementIndex;
+ mStressS = mStressE;
+ mTStress = 0;
+ mNTStress = dur;
+
+ while (j <= mElementCount)
+ {
+ Element * e = (j < mElementCount) ? &gElement[mElement[j++]] : &gElement[0];
+ int du = (j < mElementCount) ? mElement[j++] : 0;
+ int s = (j < mElementCount) ? mElement[j++] : 3;
+
+ if (s || e->mFeat & ELM_FEATURE_VWL)
+ {
+ int d = 0;
+
+ if (s)
+ mStressE.mValue = (float) s / 3;
+ else
+ mStressE.mValue = (float) 0.1;
+
+ do
+ {
+ d += du;
+ e = (j < mElementCount) ? &gElement[mElement[j++]] : &gElement[0];
+ du = mElement[j++];
+ }
+
+ while ((e->mFeat & ELM_FEATURE_VWL) && mElement[j++] == s);
+
+ mNTStress += d / 2;
+
+ break;
+ }
+
+ mNTStress += du;
+ }
+ }
+
+ for (j = 0; j < ELM_COUNT; j++)
+ {
+ tp[j] = interpolate(&start[j], &end[j], (float) currentElement->mInterpolator[j].mSteady, t, dur);
+ }
+
+ // Now call the synth for each frame
+
+ mFrame.mF0FundamentalFreq = (int)(base + (mTop - base) * interpolate(&mStressS, &mStressE, (float)0, mTStress, mNTStress));
+ mFrame.mVoicingAmpdb = mFrame.mPalallelVoicingAmpdb = (int)tp[ELM_AV];
+ mFrame.mFricationAmpdb = (int)tp[ELM_AF];
+ mFrame.mNasalZeroFreq = (int)tp[ELM_FN];
+ mFrame.mAspirationAmpdb = (int)tp[ELM_ASP];
+ mFrame.mVoicingBreathiness = (int)tp[ELM_AVC];
+ mFrame.mFormant1ParallelBandwidth = mFrame.mFormant1Bandwidth = (int)tp[ELM_B1];
+ mFrame.mFormant2ParallelBandwidth = mFrame.mFormant2Bandwidth = (int)tp[ELM_B2];
+ mFrame.mFormant3ParallelBandwidth = mFrame.mFormant3Bandwidth = (int)tp[ELM_B3];
+ mFrame.mFormant1Freq = (int)tp[ELM_F1];
+ mFrame.mFormant2Freq = (int)tp[ELM_F2];
+ mFrame.mFormant3Freq = (int)tp[ELM_F3];
+
+ // AMP_ADJ + is a kludge to get amplitudes up to klatt-compatible levels
+
+
+ //pars.mParallelNasalPoleAmpdb = AMP_ADJ + tp[ELM_AN];
+
+ mFrame.mBypassFricationAmpdb = AMP_ADJ + (int)tp[ELM_AB];
+ mFrame.mFormant5Ampdb = AMP_ADJ + (int)tp[ELM_A5];
+ mFrame.mFormant6Ampdb = AMP_ADJ + (int)tp[ELM_A6];
+ mFrame.mFormant1Ampdb = AMP_ADJ + (int)tp[ELM_A1];
+ mFrame.mFormant2Ampdb = AMP_ADJ + (int)tp[ELM_A2];
+ mFrame.mFormant3Ampdb = AMP_ADJ + (int)tp[ELM_A3];
+ mFrame.mFormant4Ampdb = AMP_ADJ + (int)tp[ELM_A4];
+
+ parwave(samp);
+
+ samp += mNspFr;
+
+ // Declination of f0 envelope 0.25Hz / cS
+ mTop -= mBaseDeclination;// 0.5;
+ }
+ }
+
+ mLastElement = currentElement;
+
+ return (int)(samp - aSamplePointer);
+}
+
+
+void klatt::init(int aBaseFrequency, float aBaseSpeed, float aBaseDeclination, int aBaseWaveform)
+{
+ mBaseF0 = aBaseFrequency;
+ mBaseSpeed = aBaseSpeed;
+ mBaseDeclination = aBaseDeclination;
+ mBaseWaveform = aBaseWaveform;
+
+ mSampleRate = 11025;
+ mF0Flutter = 0;
+ mF0FundamentalFreq = mBaseF0;
+ mFrame.mF0FundamentalFreq = mBaseF0;
+
+ int FLPhz = (950 * mSampleRate) / 10000;
+ int BLPhz = (630 * mSampleRate) / 10000;
+ mNspFr = (int)(mSampleRate * mBaseSpeed) / 1000;
+
+ mDownSampLowPassFilter.initResonator(FLPhz, BLPhz, mSampleRate);
+
+ mNPer = 0; /* LG */
+ mT0 = 0; /* LG */
+
+ mVLast = 0; /* Previous output of voice */
+ mNLast = 0; /* Previous output of random number generator */
+ mGlotLast = 0; /* Previous value of glotout */
+}
diff --git a/src/soloud/src/audiosource/speech/klatt.h b/src/soloud/src/audiosource/speech/klatt.h
new file mode 100644
index 0000000..6621090
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/klatt.h
@@ -0,0 +1,153 @@
+#ifndef KLATT_H
+#define KLATT_H
+
+#include "resonator.h"
+
+#define CASCADE_PARALLEL 1
+#define ALL_PARALLEL 2
+#define NPAR 40
+
+class klatt_frame
+{
+public:
+ int mF0FundamentalFreq; // Voicing fund freq in Hz
+ int mVoicingAmpdb; // Amp of voicing in dB, 0 to 70
+ int mFormant1Freq; // First formant freq in Hz, 200 to 1300
+ int mFormant1Bandwidth; // First formant bw in Hz, 40 to 1000
+ int mFormant2Freq; // Second formant freq in Hz, 550 to 3000
+ int mFormant2Bandwidth; // Second formant bw in Hz, 40 to 1000
+ int mFormant3Freq; // Third formant freq in Hz, 1200 to 4999
+ int mFormant3Bandwidth; // Third formant bw in Hz, 40 to 1000
+ int mFormant4Freq; // Fourth formant freq in Hz, 1200 to 4999
+ int mFormant4Bandwidth; // Fourth formant bw in Hz, 40 to 1000
+ int mFormant5Freq; // Fifth formant freq in Hz, 1200 to 4999
+ int mFormant5Bandwidth; // Fifth formant bw in Hz, 40 to 1000
+ int mFormant6Freq; // Sixth formant freq in Hz, 1200 to 4999
+ int mFormant6Bandwidth; // Sixth formant bw in Hz, 40 to 2000
+ int mNasalZeroFreq; // Nasal zero freq in Hz, 248 to 528
+ int mNasalZeroBandwidth; // Nasal zero bw in Hz, 40 to 1000
+ int mNasalPoleFreq; // Nasal pole freq in Hz, 248 to 528
+ int mNasalPoleBandwidth; // Nasal pole bw in Hz, 40 to 1000
+ int mAspirationAmpdb; // Amp of aspiration in dB, 0 to 70
+ int mNoSamplesInOpenPeriod; // # of samples in open period, 10 to 65
+ int mVoicingBreathiness; // Breathiness in voicing, 0 to 80
+ int mVoicingSpectralTiltdb; // Voicing spectral tilt in dB, 0 to 24
+ int mFricationAmpdb; // Amp of frication in dB, 0 to 80
+ int mSkewnessOfAlternatePeriods; // Skewness of alternate periods, 0 to 40 in sample#/2
+ int mFormant1Ampdb; // Amp of par 1st formant in dB, 0 to 80
+ int mFormant1ParallelBandwidth; // Par. 1st formant bw in Hz, 40 to 1000
+ int mFormant2Ampdb; // Amp of F2 frication in dB, 0 to 80
+ int mFormant2ParallelBandwidth; // Par. 2nd formant bw in Hz, 40 to 1000
+ int mFormant3Ampdb; // Amp of F3 frication in dB, 0 to 80
+ int mFormant3ParallelBandwidth; // Par. 3rd formant bw in Hz, 40 to 1000
+ int mFormant4Ampdb; // Amp of F4 frication in dB, 0 to 80
+ int mFormant4ParallelBandwidth; // Par. 4th formant bw in Hz, 40 to 1000
+ int mFormant5Ampdb; // Amp of F5 frication in dB, 0 to 80
+ int mFormant5ParallelBandwidth; // Par. 5th formant bw in Hz, 40 to 1000
+ int mFormant6Ampdb; // Amp of F6 (same as r6pa), 0 to 80
+ int mFormant6ParallelBandwidth; // Par. 6th formant bw in Hz, 40 to 2000
+ int mParallelNasalPoleAmpdb; // Amp of par nasal pole in dB, 0 to 80
+ int mBypassFricationAmpdb; // Amp of bypass fric. in dB, 0 to 80
+ int mPalallelVoicingAmpdb; // Amp of voicing, par in dB, 0 to 70
+ int mOverallGaindb; // Overall gain, 60 dB is unity, 0 to 60
+ klatt_frame();
+};
+
+class darray;
+class Element;
+
+class Slope
+{
+public:
+ float mValue; /* boundary value */
+ int mTime; /* transition time */
+ Slope()
+ {
+ mValue = 0;
+ mTime = 0;
+ }
+};
+
+
+enum KLATT_WAVEFORM
+{
+ KW_SAW,
+ KW_TRIANGLE,
+ KW_SIN,
+ KW_SQUARE,
+ KW_PULSE,
+ KW_NOISE,
+ KW_WARBLE
+};
+
+class klatt
+{
+ // resonators
+ resonator mParallelFormant1, mParallelFormant2, mParallelFormant3,
+ mParallelFormant4, mParallelFormant5, mParallelFormant6,
+ mParallelResoNasalPole, mNasalPole, mNasalZero,
+ mCritDampedGlotLowPassFilter, mDownSampLowPassFilter, mOutputLowPassFilter;
+public:
+ int mBaseF0;
+ float mBaseSpeed;
+ float mBaseDeclination;
+ int mBaseWaveform;
+
+ int mF0Flutter;
+ int mSampleRate;
+ int mNspFr;
+ int mF0FundamentalFreq; // Voicing fund freq in Hz
+ int mVoicingAmpdb; // Amp of voicing in dB, 0 to 70
+ int mSkewnessOfAlternatePeriods; // Skewness of alternate periods,0 to 40
+ int mTimeCount; // used for f0 flutter
+ int mNPer; // Current loc in voicing period 40000 samp/s
+ int mT0; // Fundamental period in output samples times 4
+ int mNOpen; // Number of samples in open phase of period
+ int mNMod; // Position in period to begin noise amp. modul
+
+ // Various amplitude variables used in main loop
+
+ float mAmpVoice; // mVoicingAmpdb converted to linear gain
+ float mAmpBypas; // mBypassFricationAmpdb converted to linear gain
+ float mAmpAspir; // AP converted to linear gain
+ float mAmpFrica; // mFricationAmpdb converted to linear gain
+ float mAmpBreth; // ATURB converted to linear gain
+
+ // State variables of sound sources
+
+ int mSkew; // Alternating jitter, in half-period units
+ float mVLast; // Previous output of voice
+ float mNLast; // Previous output of random number generator
+ float mGlotLast; // Previous value of glotout
+ float mDecay; // mVoicingSpectralTiltdb converted to exponential time const
+ float mOneMd; // in voicing one-pole ELM_FEATURE_LOW-pass filter
+
+ unsigned int mSeed; // random seed
+
+
+
+ float natural_source(int aNper);
+
+ void frame_init();
+ void flutter();
+ void pitch_synch_par_reset(int ns);
+ void parwave(short int *jwave);
+ void init(int aBaseFrequency = 1330, float aBaseSpeed = 10.0f, float aBaseDeclination = 0.5f, int aBaseWaveform = KW_SAW);
+ static int phone_to_elm(char *aPhoneme, int aCount, darray *aElement);
+
+ int mElementCount;
+ unsigned char *mElement;
+ int mElementIndex;
+ klatt_frame mFrame;
+ Element * mLastElement;
+ int mTStress;
+ int mNTStress;
+ Slope mStressS;
+ Slope mStressE;
+ float mTop;
+ void initsynth(int aElementCount,unsigned char *aElement);
+ int synth(int aSampleCount, short *aSamplePointer);
+ klatt();
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/speech/legal_readme.txt b/src/soloud/src/audiosource/speech/legal_readme.txt
new file mode 100644
index 0000000..878ec6a
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/legal_readme.txt
@@ -0,0 +1,38 @@
+The speech synth is based on rsynth by the late
+Nick Ing-Simmons (et al).
+
+He described the legal status as:
+
+ This is a text to speech system produced by
+ integrating various pieces of code and tables
+ of data, which are all (I believe) in the
+ public domain.
+
+Since then, the rsynth source code has passed legal
+checks by several open source organizations, so it
+"should" be pretty safe.
+
+The primary copyright claims seem to have to do
+with text-to-speech dictionary use, which I've
+removed completely.
+
+I've done some serious refactoring, clean-up and
+feature removal on the source, as all I need is
+"a" free, simple speech synth, not a "good"
+speech synth. Since I've removed a bunch of stuff,
+this is probably safer public domain release
+than the original.
+
+(I'm rather surprised there's no good public domain
+speech synths out there; after all, it's 2013..)
+
+I'm placing my changes in public domain as well,
+or if that's not acceptable for you, then CC0:
+http://creativecommons.org/publicdomain/zero/1.0/
+
+The SoLoud interface files (soloud_speech.*) are
+under ZLib/LibPNG license.
+
+-- Jari Komppa
+ 2013
+
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/speech/resonator.cpp b/src/soloud/src/audiosource/speech/resonator.cpp
new file mode 100644
index 0000000..6214444
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/resonator.cpp
@@ -0,0 +1,74 @@
+#include
+#include "resonator.h"
+
+#ifndef PI
+#define PI 3.1415926535897932384626433832795f
+#endif
+
+/* Convert formant freqencies and bandwidth into resonator difference equation coefficents
+ */
+void resonator::initResonator(
+ int aFrequency, /* Frequency of resonator in Hz */
+ int aBandwidth, /* Bandwidth of resonator in Hz */
+ int aSamplerate)
+{
+ float arg = (-PI / aSamplerate) * aBandwidth;
+ float r = (float)exp(arg);
+ mC = -(r * r);
+ arg = (-2.0f * PI / aSamplerate) * aFrequency;
+ mB = r * (float)cos(arg) * 2.0f;
+ mA = 1.0f - mB - mC;
+}
+
+/* Convert formant freqencies and bandwidth into anti-resonator difference equation constants
+ */
+void resonator::initAntiresonator(
+ int aFrequency, /* Frequency of resonator in Hz */
+ int aBandwidth, /* Bandwidth of resonator in Hz */
+ int aSamplerate)
+{
+ initResonator(aFrequency, aBandwidth, aSamplerate); /* First compute ordinary resonator coefficients */
+ /* Now convert to antiresonator coefficients */
+ mA = 1.0f / mA; /* a'= 1/a */
+ mB *= -mA; /* b'= -b/a */
+ mC *= -mA; /* c'= -c/a */
+}
+
+/* Generic resonator function */
+float resonator::resonate(float input)
+{
+ float x = mA * input + mB * mP1 + mC * mP2;
+ mP2 = mP1;
+ mP1 = x;
+ return x;
+}
+
+/* Generic anti-resonator function
+ Same as resonator except that a,b,c need to be set with initAntiresonator()
+ and we save inputs in p1/p2 rather than outputs.
+ There is currently only one of these - "mNasalZero"
+*/
+/* Output = (mNasalZero.a * input) + (mNasalZero.b * oldin1) + (mNasalZero.c * oldin2) */
+
+float resonator::antiresonate(float input)
+{
+ float x = mA * input + mB * mP1 + mC * mP2;
+ mP2 = mP1;
+ mP1 = input;
+ return x;
+}
+
+resonator::resonator()
+{
+ mA = mB = mC = mP1 = mP2 = 0;
+}
+
+resonator::~resonator()
+{
+}
+
+void resonator::setGain(float aG)
+{
+ mA *= aG;
+}
+
diff --git a/src/soloud/src/audiosource/speech/resonator.h b/src/soloud/src/audiosource/speech/resonator.h
new file mode 100644
index 0000000..2e75e31
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/resonator.h
@@ -0,0 +1,44 @@
+#ifndef RESONATOR_H
+#define RESONATOR_H
+
+class resonator
+{
+ float mA, mB, mC, mP1, mP2;
+public:
+
+ /* Convert formant freqencies and bandwidth into resonator difference equation coefficents
+ */
+ void initResonator(
+ int aFrequency, /* Frequency of resonator in Hz */
+ int aBandwidth, /* Bandwidth of resonator in Hz */
+ int aSamplerate);
+
+ /* Convert formant freqencies and bandwidth into anti-resonator difference equation constants
+ */
+ void initAntiresonator(
+ int aFrequency, /* Frequency of resonator in Hz */
+ int aBandwidth, /* Bandwidth of resonator in Hz */
+ int aSamplerate);
+
+ /* Set gain */
+ void setGain(float aG);
+
+ /* Generic resonator function */
+ float resonate(float input);
+
+ /* Generic anti-resonator function
+ Same as resonator except that a,b,c need to be set with initAntiresonator()
+ and we save inputs in p1/p2 rather than outputs.
+ There is currently only one of these - "mNasalZero"
+
+ Output = (mNasalZero.a * input) + (mNasalZero.b * oldin1) + (mNasalZero.c * oldin2)
+ */
+
+ float antiresonate(float input);
+
+ resonator();
+
+ ~resonator();
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/speech/soloud_speech.cpp b/src/soloud/src/audiosource/speech/soloud_speech.cpp
new file mode 100644
index 0000000..df3889e
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/soloud_speech.cpp
@@ -0,0 +1,148 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+#include "soloud.h"
+#include "soloud_speech.h"
+
+
+namespace SoLoud
+{
+ SpeechInstance::SpeechInstance(Speech *aParent)
+ {
+ mParent = aParent;
+ mSynth.init(mParent->mBaseFrequency, mParent->mBaseSpeed, mParent->mBaseDeclination, mParent->mBaseWaveform);
+ mSample = new short[mSynth.mNspFr * 100];
+ mSynth.initsynth(mParent->mElement.getSize(), (unsigned char *)mParent->mElement.getData());
+ mOffset = 10;
+ mSampleCount = 10;
+ }
+
+ SpeechInstance::~SpeechInstance()
+ {
+ delete[] mSample;
+ }
+
+ static void writesamples(short * aSrc, float * aDst, int aCount)
+ {
+ int i;
+ for (i = 0; i < aCount; i++)
+ {
+ aDst[i] = aSrc[i] * (1 / (float)0x8000);
+ }
+ }
+
+ unsigned int SpeechInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ mSynth.init(mParent->mBaseFrequency, mParent->mBaseSpeed, mParent->mBaseDeclination, mParent->mBaseWaveform);
+ unsigned int samples_out = 0;
+ if (mSampleCount > mOffset)
+ {
+ unsigned int copycount = mSampleCount - mOffset;
+ if (copycount > aSamplesToRead)
+ {
+ copycount = aSamplesToRead;
+ }
+ writesamples(mSample + mOffset, aBuffer, copycount);
+ mOffset += copycount;
+ samples_out += copycount;
+ }
+
+ while (mSampleCount >= 0 && samples_out < aSamplesToRead)
+ {
+ mOffset = 0;
+ mSampleCount = mSynth.synth(mSynth.mNspFr, mSample);
+ if (mSampleCount > 0)
+ {
+ unsigned int copycount = mSampleCount;
+ if (copycount > aSamplesToRead - samples_out)
+ {
+ copycount = aSamplesToRead - samples_out;
+ }
+ writesamples(mSample, aBuffer + samples_out, copycount);
+ mOffset += copycount;
+ samples_out += copycount;
+ }
+ }
+ return samples_out;
+ }
+
+ result SpeechInstance::rewind()
+ {
+ mSynth.init(mParent->mBaseFrequency, mParent->mBaseSpeed, mParent->mBaseDeclination, mParent->mBaseWaveform);
+ mSynth.initsynth(mParent->mElement.getSize(), (unsigned char *)mParent->mElement.getData());
+ mOffset = 10;
+ mSampleCount = 10;
+ mStreamPosition = 0.0f;
+ return 0;
+ }
+
+ bool SpeechInstance::hasEnded()
+ {
+ if (mSampleCount < 0)
+ return 1;
+ return 0;
+ }
+
+ result Speech::setParams(unsigned int aBaseFrequency, float aBaseSpeed, float aBaseDeclination, int aBaseWaveform)
+ {
+ mBaseFrequency = aBaseFrequency;
+ mBaseSpeed = aBaseSpeed;
+ mBaseDeclination = aBaseDeclination;
+ mBaseWaveform = aBaseWaveform;
+ return 0;
+ }
+
+ result Speech::setText(const char *aText)
+ {
+ if (aText == NULL)
+ return INVALID_PARAMETER;
+
+ stop();
+ mElement.clear();
+ darray phone;
+ xlate_string(aText, &phone);
+ mFrames = klatt::phone_to_elm(phone.getData(), phone.getSize(), &mElement);
+ return 0;
+ }
+
+ Speech::Speech()
+ {
+ mBaseSamplerate = 11025;
+ mFrames = 0;
+ mBaseFrequency = 1330;
+ mBaseSpeed = 10;
+ mBaseDeclination = 0.5f;
+ mBaseWaveform = KW_SQUARE;
+ }
+
+ Speech::~Speech()
+ {
+ stop();
+ }
+
+ AudioSourceInstance *Speech::createInstance()
+ {
+ return new SpeechInstance(this);
+ }
+};
diff --git a/src/soloud/src/audiosource/speech/tts.cpp b/src/soloud/src/audiosource/speech/tts.cpp
new file mode 100644
index 0000000..e65082a
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/tts.cpp
@@ -0,0 +1,1421 @@
+#include
+#include
+#include
+#include
+#include "darray.h"
+#include "tts.h"
+
+static const char *ASCII[] =
+{
+ "null", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "space", "exclamation mark", "double quote", "hash",
+ "dollar", "percent", "ampersand", "quote",
+ "open parenthesis", "close parenthesis", "asterisk", "plus",
+ "comma", "minus", "full stop", "slash",
+ "zero", "one", "two", "three",
+ "four", "five", "six", "seven",
+ "eight", "nine", "colon", "semi colon",
+ "less than", "equals", "greater than", "question mark",
+#ifndef ALPHA_IN_DICT
+ "at", "ay", "bee", "see",
+ "dee", "e", "eff", "gee",
+ "aych", "i", "jay", "kay",
+ "ell", "em", "en", "ohe",
+ "pee", "kju", "are", "es",
+ "tee", "you", "vee", "double you",
+ "eks", "why", "zed", "open bracket",
+#else /* ALPHA_IN_DICT */
+ "at", "A", "B", "C",
+ "D", "E", "F", "G",
+ "H", "I", "J", "K",
+ "L", "M", "N", "O",
+ "P", "Q", "R", "S",
+ "T", "U", "V", "W",
+ "X", "Y", "Z", "open bracket",
+#endif /* ALPHA_IN_DICT */
+ "back slash", "close bracket", "circumflex", "underscore",
+#ifndef ALPHA_IN_DICT
+ "back quote", "ay", "bee", "see",
+ "dee", "e", "eff", "gee",
+ "aych", "i", "jay", "kay",
+ "ell", "em", "en", "ohe",
+ "pee", "kju", "are", "es",
+ "tee", "you", "vee", "double you",
+ "eks", "why", "zed", "open brace",
+#else /* ALPHA_IN_DICT */
+ "back quote", "A", "B", "C",
+ "D", "E", "F", "G",
+ "H", "I", "J", "K",
+ "L", "M", "N", "O",
+ "P", "Q", "R", "S",
+ "T", "U", "V", "W",
+ "X", "Y", "Z", "open brace",
+#endif /* ALPHA_IN_DICT */
+ "vertical bar", "close brace", "tilde", "delete",
+ NULL
+};
+
+/* Context definitions */
+static const char Anything[] = "";
+/* No context requirement */
+
+static const char Nothing[] = " ";
+/* Context is beginning or end of word */
+
+static const char Silent[] = "";
+/* No phonemes */
+
+
+#define LEFT_PART 0
+#define MATCH_PART 1
+#define RIGHT_PART 2
+#define OUT_PART 3
+
+typedef const char *Rule[4];
+/* Rule is an array of 4 character pointers */
+
+
+/*0 = Punctuation */
+/*
+** LEFT_PART MATCH_PART RIGHT_PART OUT_PART
+*/
+
+
+static Rule punct_rules[] =
+{
+ {Anything, " ", Anything, " "},
+ {Anything, "-", Anything, ""},
+ {".", "'S", Anything, "z"},
+ {"#:.E", "'S", Anything, "z"},
+ {"#", "'S", Anything, "z"},
+ {Anything, "'", Anything, ""},
+ {Anything, ",", Anything, " "},
+ {Anything, ".", Anything, " "},
+ {Anything, "?", Anything, " "},
+ {Anything, "!", Anything, " "},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule A_rules[] =
+{
+ {Anything, "A", Nothing, "@"},
+ {Nothing, "ARE", Nothing, "0r"},
+ {Nothing, "AR", "O", "@r"},
+ {Anything, "AR", "#", "er"},
+ {"^", "AS", "#", "eIs"},
+ {Anything, "A", "WA", "@"},
+ {Anything, "AW", Anything, "O"},
+ {" :", "ANY", Anything, "eni"},
+ {Anything, "A", "^+#", "eI"},
+ {"#:", "ALLY", Anything, "@li"},
+ {Nothing, "AL", "#", "@l"},
+ {Anything, "AGAIN", Anything, "@gen"},
+ {"#:", "AG", "E", "IdZ"},
+ {Anything, "A", "^+:#", "&"},
+ {" :", "A", "^+ ", "eI"},
+ {Anything, "A", "^%", "eI"},
+ {Nothing, "ARR", Anything, "@r"},
+ {Anything, "ARR", Anything, "&r"},
+ {" :", "AR", Nothing, "0r"},
+ {Anything, "AR", Nothing, "3"},
+ {Anything, "AR", Anything, "0r"},
+ {Anything, "AIR", Anything, "er"},
+ {Anything, "AI", Anything, "eI"},
+ {Anything, "AY", Anything, "eI"},
+ {Anything, "AU", Anything, "O"},
+ {"#:", "AL", Nothing, "@l"},
+ {"#:", "ALS", Nothing, "@lz"},
+ {Anything, "ALK", Anything, "Ok"},
+ {Anything, "AL", "^", "Ol"},
+ {" :", "ABLE", Anything, "eIb@l"},
+ {Anything, "ABLE", Anything, "@b@l"},
+ {Anything, "ANG", "+", "eIndZ"},
+ {"^", "A", "^#", "eI"},
+ {Anything, "A", Anything, "&"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule B_rules[] =
+{
+ {Nothing, "BE", "^#", "bI"},
+ {Anything, "BEING", Anything, "biIN"},
+ {Nothing, "BOTH", Nothing, "b@UT"},
+ {Nothing, "BUS", "#", "bIz"},
+ {Anything, "BUIL", Anything, "bIl"},
+ {Anything, "B", Anything, "b"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule C_rules[] =
+{
+ {Nothing, "CH", "^", "k"},
+ {"^E", "CH", Anything, "k"},
+ {Anything, "CH", Anything, "tS"},
+ {" S", "CI", "#", "saI"},
+ {Anything, "CI", "A", "S"},
+ {Anything, "CI", "O", "S"},
+ {Anything, "CI", "EN", "S"},
+ {Anything, "C", "+", "s"},
+ {Anything, "CK", Anything, "k"},
+ {Anything, "COM", "%", "kVm"},
+ {Anything, "C", Anything, "k"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule D_rules[] =
+{
+ {"#:", "DED", Nothing, "dId"},
+ {".E", "D", Nothing, "d"},
+ {"#:^E", "D", Nothing, "t"},
+ {Nothing, "DE", "^#", "dI"},
+ {Nothing, "DO", Nothing, "mDU"},
+ {Nothing, "DOES", Anything, "dVz"},
+ {Nothing, "DOING", Anything, "duIN"},
+ {Nothing, "DOW", Anything, "daU"},
+ {Anything, "DU", "A", "dZu"},
+ {Anything, "D", Anything, "d"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule E_rules[] =
+{
+ {"#:", "E", Nothing, ""},
+ {"':^", "E", Nothing, ""},
+ {" :", "E", Nothing, "i"},
+ {"#", "ED", Nothing, "d"},
+ {"#:", "E", "D ", ""},
+ {Anything, "EV", "ER", "ev"},
+ {Anything, "E", "^%", "i"},
+ {Anything, "ERI", "#", "iri"},
+ {Anything, "ERI", Anything, "erI"},
+ {"#:", "ER", "#", "3"},
+ {Anything, "ER", "#", "er"},
+ {Anything, "ER", Anything, "3"},
+ {Nothing, "EVEN", Anything, "iven"},
+ {"#:", "E", "W", ""},
+ {"T", "EW", Anything, "u"},
+ {"S", "EW", Anything, "u"},
+ {"R", "EW", Anything, "u"},
+ {"D", "EW", Anything, "u"},
+ {"L", "EW", Anything, "u"},
+ {"Z", "EW", Anything, "u"},
+ {"N", "EW", Anything, "u"},
+ {"J", "EW", Anything, "u"},
+ {"TH", "EW", Anything, "u"},
+ {"CH", "EW", Anything, "u"},
+ {"SH", "EW", Anything, "u"},
+ {Anything, "EW", Anything, "ju"},
+ {Anything, "E", "O", "i"},
+ {"#:S", "ES", Nothing, "Iz"},
+ {"#:C", "ES", Nothing, "Iz"},
+ {"#:G", "ES", Nothing, "Iz"},
+ {"#:Z", "ES", Nothing, "Iz"},
+ {"#:X", "ES", Nothing, "Iz"},
+ {"#:J", "ES", Nothing, "Iz"},
+ {"#:CH", "ES", Nothing, "Iz"},
+ {"#:SH", "ES", Nothing, "Iz"},
+ {"#:", "E", "S ", ""},
+ {"#:", "ELY", Nothing, "li"},
+ {"#:", "EMENT", Anything, "ment"},
+ {Anything, "EFUL", Anything, "fUl"},
+ {Anything, "EE", Anything, "i"},
+ {Anything, "EARN", Anything, "3n"},
+ {Nothing, "EAR", "^", "3"},
+ {Anything, "EAD", Anything, "ed"},
+ {"#:", "EA", Nothing, "i@"},
+ {Anything, "EA", "SU", "e"},
+ {Anything, "EA", Anything, "i"},
+ {Anything, "EIGH", Anything, "eI"},
+ {Anything, "EI", Anything, "i"},
+ {Nothing, "EYE", Anything, "aI"},
+ {Anything, "EY", Anything, "i"},
+ {Anything, "EU", Anything, "ju"},
+ {Anything, "E", Anything, "e"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule F_rules[] =
+{
+ {Anything, "FUL", Anything, "fUl"},
+ {Anything, "F", Anything, "f"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule G_rules[] =
+{
+ {Anything, "GIV", Anything, "gIv"},
+ {Nothing, "G", "I^", "g"},
+ {Anything, "GE", "T", "ge"},
+ {"SU", "GGES", Anything, "gdZes"},
+ {Anything, "GG", Anything, "g"},
+ {" B#", "G", Anything, "g"},
+ {Anything, "G", "+", "dZ"},
+ {Anything, "GREAT", Anything, "greIt"},
+ {"#", "GH", Anything, ""},
+ {Anything, "G", Anything, "g"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule H_rules[] =
+{
+ {Nothing, "HAV", Anything, "h&v"},
+ {Nothing, "HERE", Anything, "hir"},
+ {Nothing, "HOUR", Anything, "aU3"},
+ {Anything, "HOW", Anything, "haU"},
+ {Anything, "H", "#", "h"},
+ {Anything, "H", Anything, ""},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule I_rules[] =
+{
+ {Nothing, "IAIN", Nothing, "I@n"},
+ {Nothing, "ING", Nothing, "IN"},
+ {Nothing, "IN", Anything, "In"},
+ {Nothing, "I", Nothing, "aI"},
+ {Anything, "IN", "D", "aIn"},
+ {Anything, "IER", Anything, "i3"},
+ {"#:R", "IED", Anything, "id"},
+ {Anything, "IED", Nothing, "aId"},
+ {Anything, "IEN", Anything, "ien"},
+ {Anything, "IE", "T", "aIe"},
+ {" :", "I", "%", "aI"},
+ {Anything, "I", "%", "i"},
+ {Anything, "IE", Anything, "i"},
+ {Anything, "I", "^+:#", "I"},
+ {Anything, "IR", "#", "aIr"},
+ {Anything, "IZ", "%", "aIz"},
+ {Anything, "IS", "%", "aIz"},
+ {Anything, "I", "D%", "aI"},
+ {"+^", "I", "^+", "I"},
+ {Anything, "I", "T%", "aI"},
+ {"#:^", "I", "^+", "I"},
+ {Anything, "I", "^+", "aI"},
+ {Anything, "IR", Anything, "3"},
+ {Anything, "IGH", Anything, "aI"},
+ {Anything, "ILD", Anything, "aIld"},
+ {Anything, "IGN", Nothing, "aIn"},
+ {Anything, "IGN", "^", "aIn"},
+ {Anything, "IGN", "%", "aIn"},
+ {Anything, "IQUE", Anything, "ik"},
+ {"^", "I", "^#", "aI"},
+ {Anything, "I", Anything, "I"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule J_rules[] =
+{
+ {Anything, "J", Anything, "dZ"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule K_rules[] =
+{
+ {Nothing, "K", "N", ""},
+ {Anything, "K", Anything, "k"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule L_rules[] =
+{
+ {Anything, "LO", "C#", "l@U"},
+ {"L", "L", Anything, ""},
+ {"#:^", "L", "%", "@l"},
+ {Anything, "LEAD", Anything, "lid"},
+ {Anything, "L", Anything, "l"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule M_rules[] =
+{
+ {Anything, "MOV", Anything, "muv"},
+ {"#", "MM", "#", "m"},
+ {Anything, "M", Anything, "m"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule N_rules[] =
+{
+ {"E", "NG", "+", "ndZ"},
+ {Anything, "NG", "R", "Ng"},
+ {Anything, "NG", "#", "Ng"},
+ {Anything, "NGL", "%", "Ng@l"},
+ {Anything, "NG", Anything, "N"},
+ {Anything, "NK", Anything, "Nk"},
+ {Nothing, "NOW", Nothing, "naU"},
+ {"#", "NG", Nothing, "Ng"},
+ {Anything, "N", Anything, "n"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule O_rules[] =
+{
+ {Anything, "OF", Nothing, "@v"},
+ {Anything, "OROUGH", Anything, "3@U"},
+ {"#:", "OR", Nothing, "3"},
+ {"#:", "ORS", Nothing, "3z"},
+ {Anything, "OR", Anything, "Or"},
+ {Nothing, "ONE", Anything, "wVn"},
+ {Anything, "OW", Anything, "@U"},
+ {Nothing, "OVER", Anything, "@Uv3"},
+ {Anything, "OV", Anything, "Vv"},
+ {Anything, "O", "^%", "@U"},
+ {Anything, "O", "^EN", "@U"},
+ {Anything, "O", "^I#", "@U"},
+ {Anything, "OL", "D", "@Ul"},
+ {Anything, "OUGHT", Anything, "Ot"},
+ {Anything, "OUGH", Anything, "Vf"},
+ {Nothing, "OU", Anything, "aU"},
+ {"H", "OU", "S#", "aU"},
+ {Anything, "OUS", Anything, "@s"},
+ {Anything, "OUR", Anything, "Or"},
+ {Anything, "OULD", Anything, "Ud"},
+ {"^", "OU", "^L", "V"},
+ {Anything, "OUP", Anything, "up"},
+ {Anything, "OU", Anything, "aU"},
+ {Anything, "OY", Anything, "oI"},
+ {Anything, "OING", Anything, "@UIN"},
+ {Anything, "OI", Anything, "oI"},
+ {Anything, "OOR", Anything, "Or"},
+ {Anything, "OOK", Anything, "Uk"},
+ {Anything, "OOD", Anything, "Ud"},
+ {Anything, "OO", Anything, "u"},
+ {Anything, "O", "E", "@U"},
+ {Anything, "O", Nothing, "@U"},
+ {Anything, "OA", Anything, "@U"},
+ {Nothing, "ONLY", Anything, "@Unli"},
+ {Nothing, "ONCE", Anything, "wVns"},
+ {Anything, "ON'T", Anything, "@Unt"},
+ {"C", "O", "N", "0"},
+ {Anything, "O", "NG", "O"},
+ {" :^", "O", "N", "V"},
+ {"I", "ON", Anything, "@n"},
+ {"#:", "ON", Nothing, "@n"},
+ {"#^", "ON", Anything, "@n"},
+ {Anything, "O", "ST ", "@U"},
+ {Anything, "OF", "^", "Of"},
+ {Anything, "OTHER", Anything, "VD3"},
+ {Anything, "OSS", Nothing, "Os"},
+ {"#:^", "OM", Anything, "Vm"},
+ {Anything, "O", Anything, "0"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule P_rules[] =
+{
+ {Anything, "PH", Anything, "f"},
+ {Anything, "PEOP", Anything, "pip"},
+ {Anything, "POW", Anything, "paU"},
+ {Anything, "PUT", Nothing, "pUt"},
+ {Anything, "P", Anything, "p"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule Q_rules[] =
+{
+ {Anything, "QUAR", Anything, "kwOr"},
+ {Anything, "QU", Anything, "kw"},
+ {Anything, "Q", Anything, "k"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule R_rules[] =
+{
+ {Nothing, "RE", "^#", "ri"},
+ {Anything, "R", Anything, "r"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule S_rules[] =
+{
+ {Anything, "SH", Anything, "S"},
+ {"#", "SION", Anything, "Z@n"},
+ {Anything, "SOME", Anything, "sVm"},
+ {"#", "SUR", "#", "Z3"},
+ {Anything, "SUR", "#", "S3"},
+ {"#", "SU", "#", "Zu"},
+ {"#", "SSU", "#", "Su"},
+ {"#", "SED", Nothing, "zd"},
+ {"#", "S", "#", "z"},
+ {Anything, "SAID", Anything, "sed"},
+ {"^", "SION", Anything, "S@n"},
+ {Anything, "S", "S", ""},
+ {".", "S", Nothing, "z"},
+ {"#:.E", "S", Nothing, "z"},
+ {"#:^##", "S", Nothing, "z"},
+ {"#:^#", "S", Nothing, "s"},
+ {"U", "S", Nothing, "s"},
+ {" :#", "S", Nothing, "z"},
+ {Nothing, "SCH", Anything, "sk"},
+ {Anything, "S", "C+", ""},
+ {"#", "SM", Anything, "zm"},
+ {"#", "SN", "'", "z@n"},
+ {Anything, "S", Anything, "s"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule T_rules[] =
+{
+ {Nothing, "THE", Nothing, "D@"},
+ {Anything, "TO", Nothing, "tu"},
+ {Anything, "THAT", Nothing, "D&t"},
+ {Nothing, "THIS", Nothing, "DIs"},
+ {Nothing, "THEY", Anything, "DeI"},
+ {Nothing, "THERE", Anything, "Der"},
+ {Anything, "THER", Anything, "D3"},
+ {Anything, "THEIR", Anything, "Der"},
+ {Nothing, "THAN", Nothing, "D&n"},
+ {Nothing, "THEM", Nothing, "Dem"},
+ {Anything, "THESE", Nothing, "Diz"},
+ {Nothing, "THEN", Anything, "Den"},
+ {Anything, "THROUGH", Anything, "Tru"},
+ {Anything, "THOSE", Anything, "D@Uz"},
+ {Anything, "THOUGH", Nothing, "D@U"},
+ {Nothing, "THUS", Anything, "DVs"},
+ {Anything, "TH", Anything, "T"},
+ {"#:", "TED", Nothing, "tId"},
+ {"S", "TI", "#N", "tS"},
+ {Anything, "TI", "O", "S"},
+ {Anything, "TI", "A", "S"},
+ {Anything, "TIEN", Anything, "S@n"},
+ {Anything, "TUR", "#", "tS3"},
+ {Anything, "TU", "A", "tSu"},
+ {Nothing, "TWO", Anything, "tu"},
+ {Anything, "T", Anything, "t"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule U_rules[] =
+{
+ {Nothing, "UN", "I", "jun"},
+ {Nothing, "UN", Anything, "Vn"},
+ {Nothing, "UPON", Anything, "@pOn"},
+ {"T", "UR", "#", "Ur"},
+ {"S", "UR", "#", "Ur"},
+ {"R", "UR", "#", "Ur"},
+ {"D", "UR", "#", "Ur"},
+ {"L", "UR", "#", "Ur"},
+ {"Z", "UR", "#", "Ur"},
+ {"N", "UR", "#", "Ur"},
+ {"J", "UR", "#", "Ur"},
+ {"TH", "UR", "#", "Ur"},
+ {"CH", "UR", "#", "Ur"},
+ {"SH", "UR", "#", "Ur"},
+ {Anything, "UR", "#", "jUr"},
+ {Anything, "UR", Anything, "3"},
+ {Anything, "U", "^ ", "V"},
+ {Anything, "U", "^^", "V"},
+ {Anything, "UY", Anything, "aI"},
+ {" G", "U", "#", ""},
+ {"G", "U", "%", ""},
+ {"G", "U", "#", "w"},
+ {"#N", "U", Anything, "ju"},
+ {"T", "U", Anything, "u"},
+ {"S", "U", Anything, "u"},
+ {"R", "U", Anything, "u"},
+ {"D", "U", Anything, "u"},
+ {"L", "U", Anything, "u"},
+ {"Z", "U", Anything, "u"},
+ {"N", "U", Anything, "u"},
+ {"J", "U", Anything, "u"},
+ {"TH", "U", Anything, "u"},
+ {"CH", "U", Anything, "u"},
+ {"SH", "U", Anything, "u"},
+ {Anything, "U", Anything, "ju"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule V_rules[] =
+{
+ {Anything, "VIEW", Anything, "vju"},
+ {Anything, "V", Anything, "v"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule W_rules[] =
+{
+ {Nothing, "WERE", Anything, "w3"},
+ {Anything, "WA", "S", "w0"},
+ {Anything, "WA", "T", "w0"},
+ {Anything, "WHERE", Anything, "hwer"},
+ {Anything, "WHAT", Anything, "hw0t"},
+ {Anything, "WHOL", Anything, "h@Ul"},
+ {Anything, "WHO", Anything, "hu"},
+ {Anything, "WH", Anything, "hw"},
+ {Anything, "WAR", Anything, "wOr"},
+ {Anything, "WOR", "^", "w3"},
+ {Anything, "WR", Anything, "r"},
+ {Anything, "W", Anything, "w"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule X_rules[] =
+{
+ {Anything, "X", Anything, "ks"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule Y_rules[] =
+{
+ {Anything, "YOUNG", Anything, "jVN"},
+ {Nothing, "YOU", Anything, "ju"},
+ {Nothing, "YES", Anything, "jes"},
+ {Nothing, "Y", Anything, "j"},
+ {"#:^", "Y", Nothing, "i"},
+ {"#:^", "Y", "I", "i"},
+ {" :", "Y", Nothing, "aI"},
+ {" :", "Y", "#", "aI"},
+ {" :", "Y", "^+:#", "I"},
+ {" :", "Y", "^#", "aI"},
+ {Anything, "Y", Anything, "I"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule Z_rules[] =
+{
+ {Anything, "Z", Anything, "z"},
+ {Anything, 0, Anything, Silent},
+};
+
+static Rule *Rules[] =
+{
+ punct_rules,
+ A_rules, B_rules, C_rules, D_rules, E_rules, F_rules, G_rules,
+ H_rules, I_rules, J_rules, K_rules, L_rules, M_rules, N_rules,
+ O_rules, P_rules, Q_rules, R_rules, S_rules, T_rules, U_rules,
+ V_rules, W_rules, X_rules, Y_rules, Z_rules
+};
+
+
+static const char *Cardinals[] =
+{
+ "zero", "one", "two", "three", "four",
+ "five", "six", "seven", "eight", "nine",
+ "ten", "eleven", "twelve", "thirteen", "fourteen",
+ "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
+};
+
+
+static const char *Twenties[] =
+{
+ "twenty", "thirty", "forty", "fifty",
+ "sixty", "seventy", "eighty", "ninety"
+};
+
+
+static const char *Ordinals[] =
+{
+ "zeroth", "first", "second", "third", "fourth",
+ "fifth", "sixth", "seventh","eighth", "ninth",
+ "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth",
+ "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"
+};
+
+
+static const char *Ord_twenties[] =
+{
+ "twentieth", "thirtieth", "fortieth", "fiftieth",
+ "sixtieth", "seventieth", "eightieth", "ninetieth"
+};
+
+
+/*
+** Translate a number to phonemes. This version is for CARDINAL numbers.
+** Note: this is recursive.
+*/
+static int xlate_cardinal(int value, darray *phone)
+{
+ int nph = 0;
+
+ if (value < 0)
+ {
+ nph += xlate_string("minus", phone);
+ value = (-value);
+
+ if (value < 0) /* Overflow! -32768 */
+ {
+ nph += xlate_string("a lot", phone);
+ return nph;
+ }
+ }
+
+ if (value >= 1000000000L)
+ /* Billions */
+ {
+ nph += xlate_cardinal(value / 1000000000L, phone);
+ nph += xlate_string("billion", phone);
+ value = value % 1000000000;
+
+ if (value == 0)
+ return nph; /* Even billion */
+
+ if (value < 100)
+ nph += xlate_string("and", phone);
+
+ /* as in THREE BILLION AND FIVE */
+ }
+
+ if (value >= 1000000L)
+ /* Millions */
+ {
+ nph += xlate_cardinal(value / 1000000L, phone);
+ nph += xlate_string("million", phone);
+ value = value % 1000000L;
+
+ if (value == 0)
+ return nph; /* Even million */
+
+ if (value < 100)
+ nph += xlate_string("and", phone);
+
+ /* as in THREE MILLION AND FIVE */
+ }
+
+ /* Thousands 1000..1099 2000..99999 */
+ /* 1100 to 1999 is eleven-hunderd to ninteen-hunderd */
+
+ if ((value >= 1000L && value <= 1099L) || value >= 2000L)
+ {
+ nph += xlate_cardinal(value / 1000L, phone);
+ nph += xlate_string("thousand", phone);
+ value = value % 1000L;
+
+ if (value == 0)
+ return nph; /* Even thousand */
+
+ if (value < 100)
+ nph += xlate_string("and", phone);
+
+ /* as in THREE THOUSAND AND FIVE */
+ }
+
+ if (value >= 100L)
+ {
+ nph += xlate_string(Cardinals[value / 100], phone);
+ nph += xlate_string("hundred", phone);
+ value = value % 100;
+
+ if (value == 0)
+ return nph; /* Even hundred */
+ }
+
+ if (value >= 20)
+ {
+ nph += xlate_string(Twenties[(value - 20) / 10], phone);
+ value = value % 10;
+
+ if (value == 0)
+ return nph; /* Even ten */
+ }
+
+ nph += xlate_string(Cardinals[value], phone);
+
+ return nph;
+}
+
+/*
+** Translate a number to phonemes. This version is for ORDINAL numbers.
+** Note: this is recursive.
+*/
+static int xlate_ordinal(int value, darray *phone)
+{
+ int nph = 0;
+
+ if (value < 0)
+ {
+ nph += xlate_string("minus", phone);
+ value = (-value);
+
+ if (value < 0) /* Overflow! -32768 */
+ {
+ nph += xlate_string("a lot", phone);
+ return nph;
+ }
+ }
+
+ if (value >= 1000000000L)
+ /* Billions */
+ {
+ nph += xlate_cardinal(value / 1000000000L, phone);
+ value = value % 1000000000;
+
+ if (value == 0)
+ {
+ nph += xlate_string("billionth", phone);
+ return nph; /* Even billion */
+ }
+
+ nph += xlate_string("billion", phone);
+
+ if (value < 100)
+ nph += xlate_string("and", phone);
+
+ /* as in THREE BILLION AND FIVE */
+ }
+
+ if (value >= 1000000L)
+ /* Millions */
+ {
+ nph += xlate_cardinal(value / 1000000L, phone);
+ value = value % 1000000L;
+
+ if (value == 0)
+ {
+ nph += xlate_string("millionth", phone);
+ return nph; /* Even million */
+ }
+
+ nph += xlate_string("million", phone);
+
+ if (value < 100)
+ nph += xlate_string("and", phone);
+
+ /* as in THREE MILLION AND FIVE */
+ }
+
+ /* Thousands 1000..1099 2000..99999 */
+ /* 1100 to 1999 is eleven-hunderd to ninteen-hunderd */
+
+ if ((value >= 1000L && value <= 1099L) || value >= 2000L)
+ {
+ nph += xlate_cardinal(value / 1000L, phone);
+ value = value % 1000L;
+
+ if (value == 0)
+ {
+ nph += xlate_string("thousandth", phone);
+ return nph; /* Even thousand */
+ }
+
+ nph += xlate_string("thousand", phone);
+
+ if (value < 100)
+ nph += xlate_string("and", phone);
+
+ /* as in THREE THOUSAND AND FIVE */
+ }
+
+ if (value >= 100L)
+ {
+ nph += xlate_string(Cardinals[value / 100], phone);
+ value = value % 100;
+
+ if (value == 0)
+ {
+ nph += xlate_string("hundredth", phone);
+ return nph; /* Even hundred */
+ }
+
+ nph += xlate_string("hundred", phone);
+ }
+
+ if (value >= 20)
+ {
+ if ((value % 10) == 0)
+ {
+ nph += xlate_string(Ord_twenties[(value - 20) / 10], phone);
+ return nph; /* Even ten */
+ }
+
+ nph += xlate_string(Twenties[(value - 20) / 10], phone);
+
+ value = value % 10;
+ }
+
+ nph += xlate_string(Ordinals[value], phone);
+
+ return nph;
+}
+
+
+static int isvowel(int chr)
+{
+ return (chr == 'A' || chr == 'E' || chr == 'I' ||
+ chr == 'O' || chr == 'U');
+}
+
+static int isconsonant(int chr)
+{
+ return (isupper(chr) && !isvowel(chr));
+}
+
+static int leftmatch(
+ const char *pattern, /* first char of pattern to match in text */
+ const char *context) /* last char of text to be matched */
+
+{
+ const char *pat;
+ const char *text;
+ int count;
+
+ if (*pattern == '\0')
+ /* null string matches any context */
+ {
+ return 1;
+ }
+
+ /* point to last character in pattern string */
+ count = (int)strlen(pattern);
+
+ pat = pattern + (count - 1);
+
+ text = context;
+
+ for (; count > 0; pat--, count--)
+ {
+ /* First check for simple text or space */
+ if (isalpha(*pat) || *pat == '\'' || *pat == ' ')
+ {
+ if (*pat != *text)
+ {
+ return 0;
+ }
+ else
+ {
+ text--;
+ continue;
+ }
+ }
+
+ switch (*pat)
+ {
+
+ case '#': /* One or more vowels */
+
+ if (!isvowel(*text))
+ return 0;
+
+ text--;
+
+ while (isvowel(*text))
+ text--;
+
+ break;
+
+ case ':': /* Zero or more consonants */
+ while (isconsonant(*text))
+ text--;
+
+ break;
+
+ case '^': /* One consonant */
+ if (!isconsonant(*text))
+ return 0;
+
+ text--;
+
+ break;
+
+ case '.': /* B, D, V, G, J, L, M, N, R, W, Z */
+ if (*text != 'B' && *text != 'D' && *text != 'V'
+ && *text != 'G' && *text != 'J' && *text != 'L'
+ && *text != 'M' && *text != 'N' && *text != 'R'
+ && *text != 'W' && *text != 'Z')
+ return 0;
+
+ text--;
+
+ break;
+
+ case '+': /* E, I or Y (front vowel) */
+ if (*text != 'E' && *text != 'I' && *text != 'Y')
+ return 0;
+
+ text--;
+
+ break;
+
+ case '%':
+
+ default:
+ fprintf(stderr, "Bad char in left rule: '%c'\n", *pat);
+
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int rightmatch(
+ const char *pattern, /* first char of pattern to match in text */
+ const char *context) /* last char of text to be matched */
+{
+ const char *pat;
+ const char *text;
+
+ if (*pattern == '\0')
+ /* null string matches any context */
+ return 1;
+
+ pat = pattern;
+
+ text = context;
+
+ for (pat = pattern; *pat != '\0'; pat++)
+ {
+ /* First check for simple text or space */
+ if (isalpha(*pat) || *pat == '\'' || *pat == ' ')
+ {
+ if (*pat != *text)
+ {
+ return 0;
+ }
+ else
+ {
+ text++;
+ continue;
+ }
+ }
+
+ switch (*pat)
+ {
+
+ case '#': /* One or more vowels */
+
+ if (!isvowel(*text))
+ return 0;
+
+ text++;
+
+ while (isvowel(*text))
+ text++;
+
+ break;
+
+ case ':': /* Zero or more consonants */
+ while (isconsonant(*text))
+ text++;
+
+ break;
+
+ case '^': /* One consonant */
+ if (!isconsonant(*text))
+ return 0;
+
+ text++;
+
+ break;
+
+ case '.': /* B, D, V, G, J, L, M, N, R, W, Z */
+ if (*text != 'B' && *text != 'D' && *text != 'V'
+ && *text != 'G' && *text != 'J' && *text != 'L'
+ && *text != 'M' && *text != 'N' && *text != 'R'
+ && *text != 'W' && *text != 'Z')
+ return 0;
+
+ text++;
+
+ break;
+
+ case '+': /* E, I or Y (front vowel) */
+ if (*text != 'E' && *text != 'I' && *text != 'Y')
+ return 0;
+
+ text++;
+
+ break;
+
+ case '%': /* ER, E, ES, ED, ING, ELY (a suffix) */
+ if (*text == 'E')
+ {
+ text++;
+
+ if (*text == 'L')
+ {
+ text++;
+
+ if (*text == 'Y')
+ {
+ text++;
+ break;
+ }
+
+ else
+ {
+ text--; /* Don't gobble L */
+ break;
+ }
+ }
+
+ else
+ if (*text == 'R' || *text == 'S' || *text == 'D')
+ text++;
+
+ break;
+ }
+
+ else
+ if (*text == 'I')
+ {
+ text++;
+
+ if (*text == 'N')
+ {
+ text++;
+
+ if (*text == 'G')
+ {
+ text++;
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ else
+ return 0;
+
+ default:
+ fprintf(stderr, "Bad char in right rule:'%c'\n", *pat);
+
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void phone_cat(darray *arg, const char *s)
+{
+ char ch;
+
+ while ((ch = *s++))
+ arg->put(ch);
+}
+
+
+static int find_rule(darray *arg, char *word, int index, Rule *rules)
+{
+ for (;;) /* Search for the rule */
+ {
+ Rule *rule;
+ const char *left,
+ *match,
+ *right,
+ *output;
+ int remainder;
+ rule = rules++;
+ match = (*rule)[1];
+
+ if (match == 0)
+ /* bad symbol! */
+ {
+ fprintf(stderr, "Error: Can't find rule for: '%c' in \"%s\"\n",
+ word[index], word);
+ return index + 1; /* Skip it! */
+ }
+
+ for (remainder = index; *match != '\0'; match++, remainder++)
+ {
+ if (*match != word[remainder])
+ break;
+ }
+
+ if (*match != '\0')
+ continue; /* found missmatch */
+
+ left = (*rule)[0];
+
+ right = (*rule)[2];
+
+ if (!leftmatch(left, &word[index - 1]))
+ continue;
+
+ if (!rightmatch(right, &word[remainder]))
+ continue;
+
+ output = (*rule)[3];
+
+ phone_cat(arg, output);
+
+ return remainder;
+ }
+}
+
+static void guess_word(darray *arg, char *word)
+{
+ int index; /* Current position in word */
+ int type; /* First letter of match part */
+ index = 1; /* Skip the initial blank */
+
+ do
+ {
+ if (isupper(word[index]))
+ type = word[index] - 'A' + 1;
+ else
+ type = 0;
+
+ index = find_rule(arg, word, index, Rules[type]);
+ }
+
+ while (word[index] != '\0');
+}
+
+
+static int NRL(const char *s, int n, darray *phone)
+{
+ int old = phone->getSize();
+ char *word = (char *) malloc(n + 3);
+ char *d = word;
+ *d++ = ' ';
+
+ while (n-- > 0)
+ {
+ char ch = *s++;
+
+ if (islower(ch))
+ ch = toupper(ch);
+
+ *d++ = ch;
+ }
+
+ *d++ = ' '; // kinda unnecessary
+
+ *d = '\0';
+ guess_word(phone, word);
+ free(word);
+ return phone->getSize() - old;
+}
+
+
+static int spell_out(const char *word, int n, darray *phone)
+{
+ int nph = 0;
+
+ while (n-- > 0)
+ {
+ nph += xlate_string(ASCII[*word++ & 0x7F], phone);
+ }
+
+ return nph;
+}
+
+static int suspect_word(const char *s, int n)
+{
+ int i = 0;
+ int seen_lower = 0;
+ int seen_upper = 0;
+ int seen_vowel = 0;
+ int last = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ char ch = *s++;
+
+ if (i && last != '-' && isupper(ch))
+ seen_upper = 1;
+
+ if (islower(ch))
+ {
+ seen_lower = 1;
+ ch = toupper(ch);
+ }
+
+ if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' || ch == 'Y')
+ seen_vowel = 1;
+
+ last = ch;
+ }
+
+ return !seen_vowel || (seen_upper && seen_lower) || !seen_lower;
+}
+
+static int xlate_word(const char *word, int n, darray *phone)
+{
+ int nph = 0;
+
+ if (*word != '[')
+ {
+ if (suspect_word(word, n))
+ return spell_out(word, n, phone);
+ else
+ {
+ nph += NRL(word, n, phone);
+ }
+ }
+
+ else
+ {
+ if ((++word)[(--n) - 1] == ']')
+ n--;
+
+ while (n-- > 0)
+ {
+ phone->put(*word++);
+ nph++;
+ }
+ }
+
+ phone->put(' ');
+
+ return nph + 1;
+}
+
+
+int xlate_string(const char *string, darray *phone)
+{
+ int nph = 0;
+ const char *s = string;
+ char ch;
+
+ while (isspace(ch = *s))
+ s++;
+
+ while ((ch = *s))
+ {
+ const char *word = s;
+
+ if (isalpha(ch))
+ {
+ while (isalpha(ch = *s) || ((ch == '\'' || ch == '-' || ch == '.') && isalpha(s[1])))
+ {
+ s++;
+ }
+
+ if (!ch || isspace(ch) || ispunct(ch) || (isdigit(ch) && !suspect_word(word, (int)(s - word))))
+ {
+ nph += xlate_word(word, (int)(s - word), phone);
+ }
+ else
+ {
+ while ((ch = *s) && !isspace(ch) && !ispunct(ch))
+ {
+ s++;
+ }
+
+ nph += spell_out(word, (int)(s - word), phone);
+ }
+ }
+ else
+ {
+ if (isdigit(ch) || (ch == '-' && isdigit(s[1])))
+ {
+ int sign = (ch == '-') ? -1 : 1;
+ int value = 0;
+
+ if (sign < 0)
+ {
+ ch = *++s;
+ }
+
+ while (isdigit(ch = *s))
+ {
+ value = value * 10 + ch - '0';
+ s++;
+ }
+
+ if (ch == '.' && isdigit(s[1]))
+ {
+ word = ++s;
+ nph += xlate_cardinal(value * sign, phone);
+ nph += xlate_string("point", phone);
+
+ while (isdigit(ch = *s))
+ {
+ s++;
+ }
+
+ nph += spell_out(word, (int)(s - word), phone);
+ }
+ else
+ {
+ /* check for ordinals, date, time etc. can go in here */
+ nph += xlate_cardinal(value * sign, phone);
+ }
+ }
+ else
+ {
+ if (ch == '[' && strchr(s, ']'))
+ {
+ const char *word = s;
+
+ while (*s && *s++ != ']')
+ /* nothing */
+ ;
+
+ nph += xlate_word(word, (int)(s - word), phone);
+ }
+ else
+ {
+ if (ispunct(ch))
+ {
+ switch (ch)
+ {
+
+ case '!':
+
+ case '?':
+
+ case '.':
+ s++;
+ phone->put('.');// (' ');
+ break;
+
+ case '"': /* change pitch ? */
+
+ case ':':
+
+ case '-':
+
+ case ';':
+
+ case ',':
+
+ case '(':
+
+ case ')':
+ s++;
+ phone->put(' ');
+ break;
+
+ case '[':
+ {
+ const char *e = strchr(s, ']');
+
+ if (e)
+ {
+ s++;
+
+ while (s < e)
+ phone->put(*s++);
+
+ s = e + 1;
+
+ break;
+ }
+ }
+ // fallthrough
+ default:
+ nph += spell_out(word, 1, phone);
+ s++;
+ break;
+ }
+ }
+ else
+ {
+ while ((ch = *s) && !isspace(ch))
+ {
+ s++;
+ }
+
+ nph += spell_out(word, (int)(s - word), phone);
+ }
+ }
+ }
+
+ while (isspace(ch = *s))
+ s++;
+ }
+ }
+
+ return nph;
+}
diff --git a/src/soloud/src/audiosource/speech/tts.h b/src/soloud/src/audiosource/speech/tts.h
new file mode 100644
index 0000000..8966e76
--- /dev/null
+++ b/src/soloud/src/audiosource/speech/tts.h
@@ -0,0 +1,5 @@
+
+extern int xlate_string (const char *string,darray *phone);
+
+
+
diff --git a/src/soloud/src/audiosource/tedsid/sid.cpp b/src/soloud/src/audiosource/tedsid/sid.cpp
new file mode 100644
index 0000000..6864d06
--- /dev/null
+++ b/src/soloud/src/audiosource/tedsid/sid.cpp
@@ -0,0 +1,623 @@
+// Issues:
+// - Filter cutoff frequencies not 100% accurate
+// - Combined waveforms of the 6581 incorrect (SID card used 8580 anyway)
+// - filter distortion not emulated
+// - no joystick or paddle support
+// - probably many more
+
+#include
+#ifndef __vita__
+#include
+#endif
+#include "sid.h"
+//#include "Tedmem.h"
+
+#define DIGIBLASTER_MULT 14
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795
+#endif
+
+// Hack to store master volume
+//unsigned int SIDsound::masterVolume = 0;
+
+//
+// Random number generator for noise waveform
+//
+
+// Test a bit. Returns 1 if bit is set.
+inline static long bit(long val, unsigned int bitnr)
+{
+ return (val >> bitnr) & 1;
+}
+
+inline void SIDsound::updateShiftReg(SIDVoice &v)
+{
+ unsigned int shiftReg = v.shiftReg;
+ unsigned int bit22 = bit(shiftReg,22);
+ unsigned int bit17 = bit(shiftReg,17);
+
+ // Shift 1 bit left
+ shiftReg = ((shiftReg) << 1);// & 0x7fffff;
+
+ // Feed bit 0
+ v.shiftReg = shiftReg | (bit22 ^ bit17);
+}
+
+inline int SIDsound::waveNoise(SIDVoice &v)
+{
+ unsigned int shiftReg = v.shiftReg;
+ // Pick out bits to make output value, left shift by 4
+ return
+ (bit(shiftReg,22) << 11) |
+ (bit(shiftReg,20) << 10) |
+ (bit(shiftReg,16) << 9) |
+ (bit(shiftReg,13) << 8) |
+ (bit(shiftReg,11) << 7) |
+ (bit(shiftReg, 7) << 6) |
+ (bit(shiftReg, 4) << 5) |
+ (bit(shiftReg, 2) << 4);
+};
+
+void SIDsound::setModel(unsigned int model)
+{
+ int i;
+
+ switch (model) {
+ case SID8580DB:
+ case SID8580:
+ for ( i=0; i<2048; i++) {
+ double x = i / 8.0;
+ //double cf = 12500.0 * i / 2048.0; // specs and YAPE
+ // approximate with a 3-degree polynomial
+ //double cf = 0.0003*x*x*x + 0.0882*x*x + 44.49*x - 38.409;
+ // approximate with a 2-degree polynomial
+ //double cf = -0.0177*x*x + 55.261*x - 55.518; // CSG 8580R4
+ double cf = -0.0156*x*x + 48.473*x - 45.074; // 8580R5
+ cutOffFreq[i] = cf <= 0 ? 0 : cf;
+ }
+ dcWave = 0x800;
+ dcMixer = 0;
+ dcVoice = 0;
+ break;
+
+ case SID6581: // R4 actually
+ for (i=0; i<1024; i++) {
+ cutOffFreq[i] = (tanh(((double)i/1.5 - 1024.0)/1024.0*M_PI) + tanh(M_PI))
+ * (6000.0 - 220.0) + 220.0;
+ }
+ for (; i<1056; i++) {
+ double x = ((double)i - 1024.0) / (1056.0 - 1003.);
+ cutOffFreq[i] = x*(1315.0 - 1003.0) + 1003.0;
+ }
+ for (; i<2048; i++) {
+ double x = ((double)i - 1056.0) / (2048.0 - 1056.0);
+ cutOffFreq[i] = //(tanh (((double)i - 2048.0)/1024.0*M_PI) + tanh(M_PI))
+ //* (20163.0 - 1315.0) + 1315.0;
+ (20163.0 - 1315.0) * x + 1315.0;
+ }
+ dcWave = 0x380;
+ dcMixer = -0xFFF*0xFF/18 >> 7;
+ dcVoice = 0x800*0xFF;
+ break;
+
+ case SID6581R1: // 6581 R1
+ for (i=0; i<1024; i++) {
+ cutOffFreq[i] = (tanh(((double)i-1024.0)/1024.0*M_PI) + tanh(M_PI))
+ * (6000.0 - 220.0) + 220.0;
+ }
+ for (; i<2048; i++) {
+ cutOffFreq[i] = (tanh (((double)i-2048.0)/1024.0*M_PI) + tanh(M_PI))
+ * (18000.0 - 4600.0) + 4600.0;
+ }
+ dcWave = 0x380;
+ dcMixer = -0xFFF*0xFF/18 >> 7;
+ dcVoice = 0x800*0xFF;
+ break;
+ }
+ setFilterCutoff();
+ model_ = model;
+}
+
+// Static data members
+const unsigned int SIDsound::RateCountPeriod[16] = {
+ 0x7F00,0x0006,0x003C,0x0330,0x20C0,0x6755,0x3800,0x500E,
+ 0x1212,0x0222,0x1848,0x59B8,0x3840,0x77E2,0x7625,0x0A93
+};
+
+const unsigned char SIDsound::envGenDRdivisors[256] = {
+ 30,30,30,30,30,30,16,16,16,16,16,16,16,16,8,8,
+ 8,8,8,8,8,8,8,8,8,8,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,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
+};
+
+void SIDsound::calcEnvelopeTable()
+{
+ // number of SIDsound envelope clocks per sample (0x1FFFFF)
+ const double deltaSampleCyclesFloat = ((double) sidBaseFreq * 256.0) / (double)sampleRate;
+ sidCyclesPerSampleInt = (unsigned int) (deltaSampleCyclesFloat + 0.5);
+}
+
+void SIDsound::setFrequency(unsigned int sid_frequency)
+{
+ switch (sid_frequency) {
+ case 0:
+ sidBaseFreq = TED_SOUND_CLOCK * 4; // 312 * 114 * 50 / 2;
+ break;
+ default:
+ sidBaseFreq = SOUND_FREQ_PAL_C64;
+ break;
+ }
+ calcEnvelopeTable();
+}
+
+void SIDsound::setSampleRate(unsigned int sampleRate_)
+{
+ sampleRate = sampleRate_;
+ calcEnvelopeTable();
+}
+
+SIDsound::SIDsound(unsigned int model, unsigned int chnlDisableMask) : enableDigiBlaster(false)
+{
+ unsigned int i;
+ masterVolume = 0;
+
+ // Link voices together
+ for (i=0; i<3; i++) {
+ voice[i].modulatedBy = &voice[(i+2)%3]; // previous voice
+ voice[i].modulatesThis = &voice[(i+1)%3]; // next voice
+ voice[i].disabled = !!((chnlDisableMask >> i) & 1);
+ }
+
+ filterCutoff = 0;
+ setModel(model);
+ setFrequency(0);
+ reset();
+}
+
+void SIDsound::reset(void)
+{
+ volume = masterVolume;
+
+ lastByteWritten = 0;
+
+ for (int v=0; v<3; v++) {
+ voice[v].wave = WAVE_NONE;
+ voice[v].egState = EG_FROZEN;
+ voice[v].accu = voice[v].add = 0;
+ voice[v].freq = voice[v].pw = 0;
+ voice[v].envCurrLevel = voice[v].envSustainLevel = 0;
+ voice[v].gate = voice[v].ring = voice[v].test = 0;
+ voice[v].filter = voice[v].sync = false;
+ voice[v].muted = 0;
+ // Initial value of internal shift register
+ voice[v].shiftReg = 0x7FFFFC;
+ voice[v].envExpCounter = 0;
+ voice[v].envAttackAdd = voice[v].envDecaySub = voice[v].envReleaseSub = 0;
+ voice[v].envCounterCompare = RateCountPeriod[0];
+ voice[v].envCounter = 0x7fff;
+ }
+
+ filterType = FILTER_NONE;
+ filterCutoff = filterResonance = 0;
+
+ Vhp = Vbp = Vlp = 0;
+ setFilterCutoff();
+ setResonance();
+
+ dcDigiBlaster = 0;
+ clockDeltaRemainder = 0;
+}
+
+inline int SIDsound::getWaveSample(SIDVoice &v)
+{
+ switch (v.wave) {
+ case WAVE_TRI:
+ return waveTriangle(v);
+ case WAVE_SAW:
+ return waveSaw(v);
+ case WAVE_PULSE:
+ return wavePulse(v);
+ case WAVE_TRISAW:
+ return waveTriSaw(v);
+ case WAVE_TRIPULSE:
+ return waveTriPulse(v);
+ case WAVE_SAWPULSE:
+ return waveSawPulse(v);
+ case WAVE_TRISAWPULSE:
+ return waveTriSawPulse(v);
+ case WAVE_NOISE:
+ return waveNoise(v);
+ default:
+ return 0x800;
+ }
+}
+
+unsigned char SIDsound::read(unsigned int adr)
+{
+ switch(adr) {
+ case 0x19:
+ case 0x1A:
+ // POTX/POTY paddle AD converters (unemulated)
+ lastByteWritten = 0;
+ return 0xFF;
+
+ // Voice 3 (only) oscillator readout
+ case 0x1B:
+ lastByteWritten = 0;
+ return (unsigned char)(getWaveSample(voice[2])>>0); // 4?
+
+ // Voice 3 EG readout
+ case 0x1C:
+ return (unsigned char)(voice[2].envCurrLevel);
+
+ case 0x1E: // Digiblaster DAC readout
+ if (enableDigiBlaster && model_ == SID8580)
+ {
+ return (unsigned char) (dcDigiBlaster >> DIGIBLASTER_MULT);
+ }
+ return lastByteWritten;
+
+ default:
+ // Write-only registers return the last value written
+ return lastByteWritten;
+ }
+}
+
+void SIDsound::write(unsigned int adr, unsigned char value)
+{
+ lastByteWritten = value;
+
+ SIDVoice &v = voice[adr/7];
+ switch (adr) {
+ case 0:
+ case 7:
+ case 14:
+ v.freq = (unsigned short)((v.freq & 0xff00) | value);
+ v.add = (unsigned int)(((double)v.freq
+ * sidBaseFreq) * 16.0 / sampleRate + 0.5);
+ break;
+
+ case 1:
+ case 8:
+ case 15:
+ v.freq = (unsigned short)((v.freq & 0xff) | (value << 8));
+ v.add = (unsigned int)(((double)v.freq
+ * sidBaseFreq) * 16.0 / sampleRate + 0.5);
+ break;
+
+ case 2:
+ case 9:
+ case 16:
+ v.pw = (unsigned short)((v.pw & 0x0f00) | value);
+ break;
+
+ case 3:
+ case 10:
+ case 17:
+ v.pw = (unsigned short)((v.pw & 0xff) | ((value & 0xf) << 8));
+ break;
+
+ case 4:
+ case 11:
+ case 18:
+ if ((value & 1) != (unsigned char) v.gate) {
+ if (value & 1) {
+ // gate on
+ v.egState = EG_ATTACK;
+ v.envCounterCompare = v.envAttackAdd;
+ } else {
+ // gate off
+#if 00
+ if (v.egState != EG_FROZEN)
+#endif
+ v.egState = EG_RELEASE;
+ v.envCounterCompare = v.envReleaseSub;
+ }
+ v.gate = value & 1;
+ }
+ v.modulatedBy->sync = value & 2;
+ v.ring = value & 4;
+ if ((value & 8) && !v.test) {
+ v.accu = 0; //(model_ >= SID8580) ? 0 : 0;
+ unsigned int bit19 = (v.shiftReg >> 19) & 1;
+ v.shiftReg = (v.shiftReg & 0x7ffffd) | ((bit19^1) << 1);
+ v.test = 0xFFF;
+ } else if (v.test && !(value & 8)) {
+ unsigned int bit0 = ((v.shiftReg >> 22) ^ (v.shiftReg >> 17)) & 0x1;
+ v.shiftReg <<= 1;
+ v.shiftReg &= 0x7fffff;
+ v.shiftReg |= bit0;
+ v.test = 0x000;
+ }
+ v.wave = (value >> 4) & 0x0F;
+ if (v.wave > 8) {
+ v.shiftReg &= 0x7fffff^(1<<22)^(1<<20)^(1<<16)^(1<<13)^(1<<11)^(1<<7)^(1<<4)^(1<<2);
+ }
+ break;
+
+ case 5:
+ case 12:
+ case 19:
+ v.envAttackAdd = value >> 4;
+ v.envDecaySub = value & 0x0F;
+ if (v.egState == EG_ATTACK)
+ v.envCounterCompare = v.envAttackAdd;
+ else if (v.egState == EG_DECAY)
+ v.envCounterCompare = v.envDecaySub;
+ break;
+
+ case 6:
+ case 13:
+ case 20:
+ v.envSustainLevel = (value >> 4) * 0x11;
+ v.envReleaseSub = value & 0x0F;
+ if (v.egState == EG_RELEASE)
+ v.envCounterCompare = v.envReleaseSub;
+ break;
+
+ case 21:
+ if ((unsigned int)(value&7) != (filterCutoff&7)) {
+ filterCutoff = (value&7)|(filterCutoff&0x7F8);
+ setFilterCutoff();
+ }
+ break;
+
+ case 22:
+ filterCutoff = (value<<3)|(filterCutoff&7);
+ setFilterCutoff();
+ break;
+
+ case 23:
+ voice[0].filter = value & 1;
+ voice[1].filter = value & 2;
+ voice[2].filter = value & 4;
+ filterResonance = (unsigned char)(value >> 4);
+ setResonance();
+ break;
+
+ case 24:
+ volume = value & 0x0F;
+ voice[2].muted = value & 0x80;
+ filterType = (unsigned char)((value >> 4) & 7);
+ break;
+
+ case 30: // Digiblaster DAC
+ if (enableDigiBlaster && model_ == SID8580)
+ {
+ dcDigiBlaster = (value ^ 0x00) << DIGIBLASTER_MULT;
+ }
+ break;
+
+ case 31: // Digiblaster ADC
+ break;
+ }
+}
+
+inline void SIDsound::setFilterCutoff()
+{
+ const double freqDomainDivCoeff = 2 * M_PI * 1.048576;
+ w0 = int(cutOffFreq[filterCutoff] * freqDomainDivCoeff);
+ // Limit cutoff to Nyquist frq to keep the sample based filter stable
+ const double NyquistFrq = double(sampleRate) / 2;
+ const double maxCutOff = NyquistFrq > 16000.0 ? 16000.0 : NyquistFrq;
+ const int w0MaxDt = int(maxCutOff * freqDomainDivCoeff); // 16000
+ if (w0 > w0MaxDt) w0 = w0MaxDt;
+}
+
+inline void SIDsound::setResonance()
+{
+ resonanceCoeffDiv1024 = (int) (1024.0/(0.707 + 1.9 * (double) filterResonance / 15.0) + 0.5); // 2.3
+}
+
+inline unsigned int SIDsound::clock()
+{
+ unsigned int count = sidCyclesPerSampleInt >> 8;
+ unsigned int tmp = sidCyclesPerSampleInt & 0xFF;
+ unsigned int newCount = clockDeltaRemainder + tmp;
+
+ if (newCount >= 0x100) {
+ clockDeltaRemainder = newCount & 0xFF;
+ count++;
+ } else {
+ clockDeltaRemainder = newCount;
+ }
+ return count;
+}
+
+// simplified version of http://bel.fi/~alankila/c64-sw/index-cpp.html
+inline int SIDsound::filterOutput(unsigned int cycles, int Vi)
+{
+ int w0deltaTime = w0 >> 6;
+ Vi >>= 7;
+ unsigned int count = cycles;
+
+ do {
+ int dVlp = (w0deltaTime * Vbp >> 14);
+ Vlp -= dVlp;
+ int dVbp = (w0deltaTime * Vhp >> 14);
+ Vbp -= dVbp;
+ Vhp = (Vbp * resonanceCoeffDiv1024 >> 10) - Vlp - Vi;
+ } while (--count);
+
+ int Vf;
+
+ switch (filterType) {
+ default:
+ case FILTER_NONE:
+ Vf = 0;
+ break;
+ case FILTER_LP:
+ Vf = Vlp;
+ break;
+ case FILTER_BP:
+ Vf = Vbp;
+ break;
+ case FILTER_LPBP:
+ Vf = Vlp + Vbp;
+ break;
+ case FILTER_HP:
+ Vf = Vhp;
+ break;
+ case FILTER_NOTCH:
+ Vf = Vlp + Vhp;
+ break;
+ case FILTER_HPBP:
+ Vf = Vbp + Vhp;
+ break;
+ case FILTER_ALL:
+ Vf = Vlp + Vbp + Vhp;
+ break;
+ }
+ return Vf << 7;
+}
+
+// Envelope based on:
+// http://blog.kevtris.org/?p=13
+inline int SIDsound::doEnvelopeGenerator(unsigned int cycles, SIDVoice &v)
+{
+ unsigned int count = cycles;
+
+ do {
+ unsigned int LFSR = v.envCounter;
+ if (LFSR != RateCountPeriod[v.envCounterCompare]) {
+ const unsigned int feedback = ((LFSR >> 14) ^ (LFSR >> 13)) & 1;
+ LFSR = ((LFSR << 1) | feedback) & 0x7FFF;
+ v.envCounter = LFSR;
+ } else {
+ // LFSR = 0x7fff reset LFSR
+ v.envCounter = 0x7fff;
+
+ if (v.egState == EG_ATTACK || ++v.envExpCounter == envGenDRdivisors[v.envCurrLevel]) {
+
+ v.envExpCounter = 0;
+
+ switch (v.egState) {
+
+ case EG_ATTACK:
+ // According to Bob Yannes, Attack is linear...
+ if ( ((++v.envCurrLevel) & 0xFF) == 0xFF) {
+ v.egState = EG_DECAY;
+ v.envCounterCompare = v.envDecaySub;
+ }
+ break;
+
+ case EG_DECAY:
+ if (v.envCurrLevel != v.envSustainLevel) {
+ v.envCurrLevel--;
+ v.envCurrLevel &= 0xFF;
+ if (!v.envCurrLevel)
+ v.egState = EG_FROZEN;
+ }
+ break;
+
+ case EG_RELEASE:
+ v.envCurrLevel = (v.envCurrLevel - 1) & 0xFF;
+ if (!v.envCurrLevel)
+ v.egState = EG_FROZEN;
+ break;
+
+ case EG_FROZEN:
+ v.envCurrLevel = 0;
+ break;
+ }
+ }
+ }
+ } while (--count);
+
+ return v.envCurrLevel & 0xFF; // envelope is 8 bits
+}
+
+void SIDsound::calcSamples(short *buf, long accu)
+{
+ for (;accu--;) {
+ // Outputs for normal and filtered sounds
+ int sumFilteredOutput = 0;
+ int sumOutput = 0;
+ int sample;
+
+ const unsigned int cyclesToDo = clock();
+ // Loop for the three voices
+ int j = 2;
+ do {
+ SIDVoice &v = voice[j];
+ int envelope = doEnvelopeGenerator(cyclesToDo, v);
+ // Waveform generator
+ if (!v.test) {
+#if 1
+ unsigned int accPrev = v.accu;
+ // Update accumulator
+ v.accu += v.add;
+ // FIXME Apply ring modulation.
+ if (v.sync && !(accPrev & 0x8000000) && (v.accu & 0x8000000)
+ )
+#else
+ v.accPrev = v.accu;
+ // Update accumulator if test bit not set
+ v.accu += v.add;
+ unsigned int accPrev = v.accPrev;
+ if (v.sync && !(v.accPrev & 0x8000000) && (v.accu & 0x8000000)
+ && !( v.modulatedBy->sync && !(v.modulatedBy->accPrev & 0x800000) &&
+ (v.modulatedBy->accu & 0x800000))
+ )
+#endif
+ v.modulatesThis->accu = 0;
+ if (v.freq) {
+ unsigned int accNext = accPrev;
+ unsigned int freq = v.freq << 4;
+ do {
+ accNext += freq;
+ // noise shift register is updating even when waveform is not selected
+ if (!(accPrev & 0x0800000) && (accNext & 0x0800000))
+ updateShiftReg(v);
+ accPrev = accNext;
+ } while ( accNext < v.accu );
+ }
+ // real accu is 24 bit but we use FP integer arithmetic
+ v.accu &= 0xFFFFFFF;
+ }
+ int output = v.disabled ? 0x0800 : getWaveSample(v);
+
+ if (v.filter)
+ sumFilteredOutput += (output - dcWave) * envelope + dcVoice;
+ else {
+ if (v.muted)
+ sumOutput += (0x0800 - dcWave) * envelope + dcVoice;
+ else
+ sumOutput += (output - dcWave) * envelope + dcVoice;
+ }
+ } while (j--);
+
+ int accu = (sumOutput + filterOutput(cyclesToDo, sumFilteredOutput)
+ + dcMixer + dcDigiBlaster) * volume;
+
+#if 1
+ sample = accu >> 12;
+#else
+ unsigned int interPolationFac = (clockDeltaRemainder - sidCyclesPerSampleInt) & 0xFF;
+ accu >>= 7;
+ sample = (prevAccu * (0xFF ^ interPolationFac) + accu * (interPolationFac)) >> 12;
+ prevAccu = accu;
+#endif
+
+ *buf++ = (short) sample;
+ }
+}
+
+SIDsound::~SIDsound()
+{
+ masterVolume = volume;
+}
diff --git a/src/soloud/src/audiosource/tedsid/sid.h b/src/soloud/src/audiosource/tedsid/sid.h
new file mode 100644
index 0000000..23d0cb7
--- /dev/null
+++ b/src/soloud/src/audiosource/tedsid/sid.h
@@ -0,0 +1,180 @@
+#ifndef _SID_H
+#define _SID_H
+
+#define SOUND_FREQ_PAL_C64 985248
+#define TED_SOUND_CLOCK (221680)
+
+enum {
+ SID6581 = 0,
+ SID8580,
+ SID8580DB,
+ SID6581R1
+};
+
+// SID class
+class SIDsound
+{
+public:
+ SIDsound(unsigned int model, unsigned int chnlDisableMask);
+ virtual ~SIDsound();
+ virtual void reset();
+ virtual void setReplayFreq() {
+ calcEnvelopeTable();
+ };
+ void setModel(unsigned int model);
+ void setFrequency(unsigned int sid_frequency);
+ void setSampleRate(unsigned int sampleRate_);
+ void calcEnvelopeTable();
+ unsigned char read(unsigned int adr);
+ void write(unsigned int adr, unsigned char byte);
+ void calcSamples(short *buf, long count);
+ void enableDisableChannel(unsigned int ch, bool enabled) {
+ voice[ch].disabled = !enabled;
+ }
+
+private:
+
+ // SIDsound waveforms
+ enum {
+ WAVE_NONE, WAVE_TRI, WAVE_SAW, WAVE_TRISAW, WAVE_PULSE,
+ WAVE_TRIPULSE, WAVE_SAWPULSE, WAVE_TRISAWPULSE, WAVE_NOISE
+ };
+ // Envelope Genarator states
+ enum {
+ EG_FROZEN, EG_ATTACK, EG_DECAY, EG_RELEASE
+ };
+ // Filter types
+ enum {
+ FILTER_NONE, FILTER_LP, FILTER_BP, FILTER_LPBP, FILTER_HP, FILTER_NOTCH, FILTER_HPBP, FILTER_ALL
+ };
+ // Class for SID voices
+ class SIDVoice {
+ public:
+ int wave; // the selected waveform
+ int egState; // state of the EG
+ SIDVoice *modulatedBy; // the voice that modulates this one
+ SIDVoice *modulatesThis;// the voice that is modulated by this one
+
+ unsigned int accu; // accumulator of the waveform generator, 8.16 fixed
+ unsigned int accPrev; // previous accu value (for ring modulation)
+ unsigned int add; // added to the accumulator for each sample
+ unsigned int shiftReg; // shift register for noise waveform
+
+ unsigned short freq; // voice frequency
+ unsigned short pw; // pulse-width value
+
+ unsigned int envAttackAdd;
+ unsigned int envDecaySub;
+ unsigned int envSustainLevel;
+ unsigned int envReleaseSub;
+ unsigned int envCurrLevel;
+ unsigned int envCounter;
+ unsigned int envExpCounter;
+ unsigned int envCounterCompare;
+
+ unsigned int gate; // EG gate flag
+ unsigned int ring; // ring modulation flag
+ unsigned int test; // test flag
+ unsigned int filter; // voice filtered flag
+ unsigned int muted; // voice muted flag (only for 3rd voice)
+ bool disabled; // voice disabled
+
+ // This bit is set for the modulating voice,
+ // not for the modulated one (compared to the real one)
+ unsigned int sync; // sync modulation flag
+ };
+ int volume; // SID Master volume
+ unsigned int sidBaseFreq; // SID base frequency
+ unsigned int sidCyclesPerSampleInt;
+ unsigned int clockDeltaRemainder;
+ int dcMixer; // different for 6581 and 8580 (constant level output for digi)
+ int dcVoice;
+ int dcWave;
+ int dcDigiBlaster;
+ //int extIn;
+ //
+ unsigned int clock();
+ // Wave generator functions
+ inline static int waveTriangle(SIDVoice &v);
+ inline static int waveSaw(SIDVoice &v);
+ inline static int wavePulse(SIDVoice &v);
+ inline static int waveTriSaw(SIDVoice &v);
+ inline static int waveTriPulse(SIDVoice &v);
+ inline static int waveSawPulse(SIDVoice &v);
+ inline static int waveTriSawPulse(SIDVoice &v);
+ inline static int waveNoise(SIDVoice &v);
+ inline static int getWaveSample(SIDVoice &v);
+ inline void updateShiftReg(SIDVoice &v);
+ // Envelope
+ inline int doEnvelopeGenerator(unsigned int cycles, SIDVoice &v);
+ static const unsigned int RateCountPeriod[16]; // Factors for A/D/S/R Timing
+ static const unsigned char envGenDRdivisors[256]; // For exponential approximation of D/R
+ /*static*/ unsigned int masterVolume;
+ // voice array for the 3 channels
+ SIDVoice voice[3];
+ // filter stuff
+ unsigned char filterType; // filter type
+ unsigned int filterCutoff; // SID filter frequency
+ unsigned char filterResonance; // filter resonance (0..15)
+ double cutOffFreq[2048]; // filter cutoff frequency register
+ int resonanceCoeffDiv1024; // filter resonance * 1024
+ int w0; // filter cutoff freq
+ void setResonance();
+ void setFilterCutoff();
+ int filterOutput(unsigned int cycles, int Vi);
+ int Vhp; // highpass
+ int Vbp; // bandpass
+ int Vlp; // lowpass
+ //
+ unsigned char lastByteWritten;// Last value written to the SID
+ int model_;
+ bool enableDigiBlaster;
+ unsigned int sampleRate;
+};
+
+/*
+ Wave outputs
+*/
+inline int SIDsound::waveTriangle(SIDVoice &v)
+{
+ unsigned int msb = (v.ring ? v.accu ^ v.modulatedBy->accu : v.accu)
+ & 0x8000000;
+ // triangle wave 15 bit only
+ return ((msb ? ~v.accu : v.accu) >> 15) & 0xFFE;
+}
+
+inline int SIDsound::waveSaw(SIDVoice &v)
+{
+ return (v.accu >> 16) & 0xFFF;
+}
+
+inline int SIDsound::wavePulse(SIDVoice &v)
+{
+ // square wave starts high
+ return (v.test | ((v.accu >> 16) >= v.pw ? 0xFFF : 0x000));
+}
+
+inline int SIDsound::waveTriSaw(SIDVoice &v)
+{
+ unsigned int sm = (waveTriangle(v)) & (waveSaw(v));
+ return (sm>>1) & (sm<<1);
+}
+
+inline int SIDsound::waveTriPulse(SIDVoice &v)
+{
+ unsigned int sm = (waveTriangle(v)) & (wavePulse(v));
+ return (sm>>1) & (sm<<1);
+}
+
+inline int SIDsound::waveSawPulse(SIDVoice &v)
+{
+ return (waveSaw(v)) & (wavePulse(v));
+}
+
+inline int SIDsound::waveTriSawPulse(SIDVoice &v)
+{
+ unsigned int sm = (waveTriangle(v)) & (waveSaw(v)) & (wavePulse(v));
+ return (sm>>1) & (sm<<1);
+}
+
+#endif
diff --git a/src/soloud/src/audiosource/tedsid/soloud_tedsid.cpp b/src/soloud/src/audiosource/tedsid/soloud_tedsid.cpp
new file mode 100644
index 0000000..4e5f844
--- /dev/null
+++ b/src/soloud/src/audiosource/tedsid/soloud_tedsid.cpp
@@ -0,0 +1,256 @@
+/*
+TED/SID module for SoLoud audio engine
+Copyright (c) 2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include
+#include
+#include "sid.h"
+#include "ted.h"
+#include "soloud_tedsid.h"
+#include "soloud_file.h"
+
+namespace SoLoud
+{
+
+ TedSidInstance::TedSidInstance(TedSid *aParent)
+ {
+ mParent = aParent;
+ mSampleCount = 0;
+ mSID = new SIDsound(mParent->mModel, 0);
+ mSID->setFrequency(0);
+ mSID->setSampleRate(TED_SOUND_CLOCK);
+ mSID->setFrequency(1);
+
+ mTED = new TED();
+ mTED->oscillatorInit();
+
+ mNextReg = 100; // NOP
+ mNextVal = 0;
+ int i;
+ for (i = 0; i < 128; i++)
+ mRegValues[i] = 0;
+ }
+
+ unsigned int TedSidInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ unsigned int i;
+ for (i = 0; i < aSamplesToRead; i++)
+ {
+ tick();
+ short sample;
+ mSID->calcSamples(&sample, 1);
+ short tedsample = 0;
+ mTED->renderSound(1, &tedsample);
+ aBuffer[i] = (sample + tedsample) / 8192.0f;
+ mSampleCount--;
+ }
+ return aSamplesToRead;
+ }
+
+ void TedSidInstance::tick()
+ {
+ if (mParent->mFile == 0)
+ return;
+
+ while (mSampleCount == 0)
+ {
+ mRegValues[mNextReg] = mNextVal;
+ if (mNextReg < 64)
+ {
+ mSID->write(mNextReg, mNextVal);
+ }
+ else
+ if (mNextReg < 64 + 5)
+ {
+ mTED->writeSoundReg(mNextReg - 64, mNextVal);
+ }
+// mSampleCount = mParent->mFile->read16();
+ mNextVal = mParent->mFile->read8();
+ mNextReg = mParent->mFile->read8();
+ if (mNextReg & 0x80)
+ {
+ // timestamp!
+ mSampleCount = ((int)(mNextReg & 0x7f) << 8) | mNextVal;
+ mNextVal = mParent->mFile->read8();
+ mNextReg = mParent->mFile->read8();
+ }
+ if (mParent->mFile->eof())
+ mParent->mFile->seek(8);
+ }
+ }
+
+ float TedSidInstance::getInfo(unsigned int aInfoKey)
+ {
+ return (float)mRegValues[aInfoKey & 127];
+ }
+
+ bool TedSidInstance::hasEnded()
+ {
+ return 0;
+ }
+
+ TedSidInstance::~TedSidInstance()
+ {
+ delete mSID;
+ delete mTED;
+ }
+
+ TedSid::TedSid()
+ {
+ mBaseSamplerate = TED_SOUND_CLOCK;
+ mChannels = 1;
+ mFile = 0;
+ mFileOwned = false;
+ }
+
+ TedSid::~TedSid()
+ {
+ stop();
+ if (mFileOwned)
+ delete mFile;
+ }
+
+ result TedSid::loadMem(unsigned char *aMem, unsigned int aLength, bool aCopy, bool aTakeOwnership)
+ {
+ if (!aMem || aLength == 0)
+ return INVALID_PARAMETER;
+ MemoryFile *mf = new MemoryFile;
+ if (!mf)
+ return OUT_OF_MEMORY;
+ int res = mf->openMem(aMem, aLength, aCopy, aTakeOwnership);
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+ res = loadFile(mf);
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+ mFileOwned = aCopy || aTakeOwnership;
+
+ return SO_NO_ERROR;
+ }
+
+ result TedSid::load(const char *aFilename)
+ {
+ if (!aFilename)
+ return INVALID_PARAMETER;
+ DiskFile *df = new DiskFile;
+ if (!df) return OUT_OF_MEMORY;
+ int res = df->open(aFilename);
+ if (res != SO_NO_ERROR)
+ {
+ delete df;
+ return res;
+ }
+ res = loadFile(df);
+ if (res != SO_NO_ERROR)
+ {
+ delete df;
+ return res;
+ }
+ mFileOwned = true;
+ return SO_NO_ERROR;
+ }
+
+ result TedSid::loadToMem(const char *aFilename)
+ {
+ if (!aFilename)
+ return INVALID_PARAMETER;
+ MemoryFile *mf = new MemoryFile;
+ if (!mf) return OUT_OF_MEMORY;
+ int res = mf->openToMem(aFilename);
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+ res = loadFile(mf);
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+ mFileOwned = true;
+ return SO_NO_ERROR;
+ }
+
+ result TedSid::loadFileToMem(File *aFile)
+ {
+ if (!aFile)
+ return INVALID_PARAMETER;
+ MemoryFile *mf = new MemoryFile;
+ if (!mf) return OUT_OF_MEMORY;
+ int res = mf->openFileToMem(aFile);
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+ res = loadFile(mf);
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+ mFileOwned = true;
+ return SO_NO_ERROR;
+ }
+
+ result TedSid::loadFile(File *aFile)
+ {
+ if (aFile == NULL)
+ return INVALID_PARAMETER;
+ if (mFileOwned)
+ delete mFile;
+ // Expect a file wih header and at least one reg write
+ if (aFile->length() < 4+4+2+2) return FILE_LOAD_FAILED;
+
+ aFile->seek(0);
+ if (aFile->read8() != 'D') return FILE_LOAD_FAILED;
+ if (aFile->read8() != 'u') return FILE_LOAD_FAILED;
+ if (aFile->read8() != 'm') return FILE_LOAD_FAILED;
+ if (aFile->read8() != 'p') return FILE_LOAD_FAILED;
+ if (aFile->read8() != 0) return FILE_LOAD_FAILED;
+ mModel = aFile->read8();
+ aFile->seek(8);
+
+ mFile = aFile;
+ mFileOwned = false;
+
+
+ return SO_NO_ERROR;
+ }
+
+
+ AudioSourceInstance * TedSid::createInstance()
+ {
+ return new TedSidInstance(this);
+ }
+
+};
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/tedsid/ted.cpp b/src/soloud/src/audiosource/tedsid/ted.cpp
new file mode 100644
index 0000000..21d5978
--- /dev/null
+++ b/src/soloud/src/audiosource/tedsid/ted.cpp
@@ -0,0 +1,236 @@
+#include
+#include "ted.h"
+
+
+#define PRECISION 0
+#define OSCRELOADVAL (0x3FF << PRECISION)
+#define TED_SOUND_CLOCK (221680)
+
+TED::TED()
+{
+ sampleRate = TED_SOUND_CLOCK;
+ masterVolume = 8;
+ Volume = 8;
+ Snd1Status = 0;
+ Snd2Status = 0;
+ SndNoiseStatus = 0;
+ DAStatus = 0;
+ Freq1 = 0;
+ Freq2 = 0;
+ NoiseCounter = 0;
+ FlipFlop[0] = FlipFlop[1] = 0;
+ dcOutput[0] = dcOutput[1] = 0;
+ oscCount[0] = oscCount[1] = 0;
+ OscReload[0] = OscReload[1] = 0;
+ waveForm[0] = waveForm[1] = 0;
+ oscStep = 0;
+ memset(noise, 0, sizeof(noise));
+ channelMask[0] = channelMask[1] = 0;
+ vol = 0;
+}
+
+void TED::enableChannel(unsigned int channel, bool enable)
+{
+ channelMask[channel % 3] = enable ? -1 : 0;
+}
+
+inline void TED::setFreq(unsigned int channel, int freq)
+{
+ dcOutput[channel] = (freq == 0x3FE) ? 1 : 0;
+ OscReload[channel] = ((freq + 1)&0x3FF) << PRECISION;
+}
+
+void TED::oscillatorReset()
+{
+ FlipFlop[0] = dcOutput[0] = 0;
+ FlipFlop[1] = dcOutput[1] = 0;
+ oscCount[0] = 0;
+ oscCount[1] = 0;
+ NoiseCounter = 0;
+ Freq1 = Freq2 = 0;
+ DAStatus = Snd1Status = Snd2Status = 0;
+}
+
+// call only once!
+void TED::oscillatorInit()
+{
+ oscillatorReset();
+ /* initialise im with 0xa8 */
+ int im = 0xa8;
+ for (unsigned int i = 0; i<256; i++) {
+ noise[i] = im & 1;
+ im = (im<<1)+(1^((im>>7)&1)^((im>>5)&1)^((im>>4)&1)^((im>>1)&1));
+ }
+ oscStep = (1 << PRECISION) << 0;
+
+ // set player specific parameters
+ waveForm[0] = waveForm[1] = 1;
+ masterVolume = 8;
+ enableChannel(0, true);
+ enableChannel(1, true);
+ enableChannel(2, true);
+}
+
+void TED::writeSoundReg(unsigned int reg, unsigned char value)
+{
+
+ switch (reg) {
+ case 0:
+ Freq1 = (Freq1 & 0x300) | value;
+ setFreq(0, Freq1);
+ break;
+ case 1:
+ Freq2 = (Freq2 & 0x300) | value;
+ setFreq(1, Freq2);
+ break;
+ case 2:
+ Freq2 = (Freq2 & 0xFF) | (value << 8);
+ setFreq(1, Freq2);
+ break;
+ case 3:
+ DAStatus = value & 0x80;
+ if (DAStatus) {
+ FlipFlop[0] = 1;
+ FlipFlop[1] = 1;
+ oscCount[0] = OscReload[0];
+ oscCount[1] = OscReload[1];
+ NoiseCounter = 0xFF;
+ }
+ Volume = value & 0x0F;
+ if (Volume > 8) Volume = 8;
+ Volume = (Volume << 8) * masterVolume / 10;
+ Snd1Status = value & 0x10;
+ Snd2Status = value & 0x20;
+ SndNoiseStatus = value & 0x40;
+ break;
+ case 4:
+ Freq1 = (Freq1 & 0xFF) | (value << 8);
+ setFreq(0, Freq1);
+ break;
+ }
+}
+
+inline unsigned int TED::waveSquare(unsigned int channel)
+{
+ return Volume;
+}
+
+inline unsigned int TED::waveSawTooth(unsigned int channel)
+{
+ unsigned int mod;
+
+#if 0
+ int msb = OSCRELOADVAL + 1 - OscReload[channel];
+ int diff = 2 * msb - int(FlipFlop[channel]) * msb - int(oscCount[channel]) + int(OscReload[channel]);
+ //if (diff < 0) diff = 0;
+ //if (oscCount[channel] >= 0x3fa) diff = 0;
+ mod = (Volume * diff) / (2 * msb);
+#else
+ int diff = int(oscCount[channel]) - int(OscReload[channel]);
+ if (diff < 0) diff = 0;
+ mod = (Volume * diff) / (OSCRELOADVAL + 1 - OscReload[channel]);
+#endif
+ return mod;
+}
+
+inline unsigned int TED::waveTriangle(unsigned int channel)
+{
+ unsigned int mod;
+ int msb;
+
+#if 0
+ msb = OSCRELOADVAL + 1 - OscReload[channel];
+ int diff = FlipFlop[channel] ? int(oscCount[channel]) - int(OscReload[channel])
+ : int(OSCRELOADVAL) - int(oscCount[channel]);
+ //if (diff < 0) diff = 0;
+ //if (oscCount[channel] >= 0x3fa) diff = 0;
+ mod = (3 * Volume * diff / msb / 2);
+#else
+ /*
+ msb = (OscReload[channel] + OSCRELOADVAL) / 2;
+ int diff = oscCount[channel] < msb ? oscCount[channel] - OscReload[channel] : OSCRELOADVAL - oscCount[channel];
+ mod = (2 * diff * Volume / (OSCRELOADVAL - OscReload[channel] + 1));
+ if (mod > Volume) mod = Volume;
+ */
+ msb = (OscReload[channel] + OSCRELOADVAL) / 2;
+ mod = oscCount[channel] < msb ? oscCount[channel] : (oscCount[channel] - msb);
+ mod = (mod * Volume / msb);
+#endif
+ return mod;
+}
+
+inline unsigned int TED::getWaveSample(unsigned int channel, unsigned int wave)
+{
+ unsigned int sm;
+
+ switch (wave) {
+ default:
+ case 1: // square
+ return waveSquare(channel);
+ break;
+ case 2: // sawtooth
+ return waveSawTooth(channel);
+ break;
+ case 4: // triangle
+ return waveTriangle(channel);
+ break;
+
+ // combined waveforms á la SID
+ case 3: // square + sawtooth
+ sm = waveSawTooth(channel) + waveSquare(channel);
+ return sm /= 2;
+ break;
+ case 5: // square + triangle
+ sm = waveTriangle(channel) + waveSquare(channel);
+ return sm /= 2;
+ break;
+ case 6: // sawtooth + triangle
+ sm = waveTriangle(channel) + waveSawTooth(channel);
+ return sm /= 2;
+ break;
+ case 7: // square + sawtooth + triangle
+ sm = waveTriangle(channel) + waveSawTooth(channel) + waveSquare(channel);
+ return sm /= 3;
+ break;
+ }
+}
+
+void TED::renderSound(unsigned int nrsamples, short *buffer)
+{
+ // Calculate the buffer...
+ if (DAStatus) {// digi?
+ short sample = 0;//audiohwspec->silence;
+ if (Snd1Status) sample = Volume;
+ if (Snd2Status) sample += Volume;
+ for (;nrsamples--;) {
+ *buffer++ = sample & channelMask[2];
+ }
+ } else {
+ unsigned int result;
+ for (;nrsamples--;) {
+ // Channel 1
+ if (dcOutput[0]) {
+ FlipFlop[0] = 1;
+ } else if ((oscCount[0] += oscStep) >= OSCRELOADVAL) {
+ FlipFlop[0] ^= 1;
+ oscCount[0] = OscReload[0] + (oscCount[0] - OSCRELOADVAL);
+ }
+ // Channel 2
+ if (dcOutput[1]) {
+ FlipFlop[1] = 1;
+ } else if ((oscCount[1] += oscStep) >= OSCRELOADVAL) {
+ NoiseCounter = (NoiseCounter + 1) & 0xFF;
+ FlipFlop[1] ^= 1;
+ oscCount[1] = OscReload[1] + (oscCount[1] - OSCRELOADVAL);
+ }
+ result = (Snd1Status && FlipFlop[0]) ? (getWaveSample(0, waveForm[0]) & channelMask[0]) : 0;
+ if (Snd2Status && FlipFlop[1]) {
+ result += getWaveSample(1, waveForm[1]) & channelMask[1];
+ } else if (SndNoiseStatus && noise[NoiseCounter] & channelMask[2]) {
+ result += Volume;
+ }
+ *buffer++ = result;
+ } // for
+ }
+}
+
diff --git a/src/soloud/src/audiosource/tedsid/ted.h b/src/soloud/src/audiosource/tedsid/ted.h
new file mode 100644
index 0000000..8f2b196
--- /dev/null
+++ b/src/soloud/src/audiosource/tedsid/ted.h
@@ -0,0 +1,44 @@
+
+class TED
+{
+public:
+ unsigned int masterVolume;
+ int Volume;
+ int Snd1Status;
+ int Snd2Status;
+ int SndNoiseStatus;
+ int DAStatus;
+ unsigned short Freq1;
+ unsigned short Freq2;
+ int NoiseCounter;
+ int FlipFlop[2];
+ int dcOutput[2];
+ int oscCount[2];
+ int OscReload[2];
+ int waveForm[2];
+ int oscStep;
+ int sampleRate;
+ unsigned char noise[256]; // 0-8
+ unsigned int channelMask[3];
+ int vol;
+
+ TED();
+ void enableChannel(unsigned int channel, bool enable);
+ void setFreq(unsigned int channel, int freq);
+ void oscillatorReset();
+ void oscillatorInit();
+ void writeSoundReg(unsigned int reg, unsigned char value);
+ void storeToBuffer(short *buffer, unsigned int count);
+ unsigned int waveSquare(unsigned int channel);
+ unsigned int waveSawTooth(unsigned int channel);
+ unsigned int waveTriangle(unsigned int channel);
+ unsigned int getWaveSample(unsigned int channel, unsigned int wave);
+ void renderSound(unsigned int nrsamples, short *buffer);
+ void setMasterVolume(unsigned int shift);
+ void selectWaveForm(unsigned int channel, unsigned int wave);
+ void setplaybackSpeed(unsigned int speed);
+ unsigned int getTimeSinceLastReset();
+ void setSampleRate(unsigned int value);
+ void setFilterOrder(unsigned int value);
+ void initFilter(unsigned int sampleRate_, unsigned int filterOrder_);
+};
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/vic/soloud_vic.cpp b/src/soloud/src/audiosource/vic/soloud_vic.cpp
new file mode 100644
index 0000000..0d7ac60
--- /dev/null
+++ b/src/soloud/src/audiosource/vic/soloud_vic.cpp
@@ -0,0 +1,168 @@
+/*
+SoLoud audio engine
+Copyright (c) 2015 Jari Komppa
+
+VIC 6560/6561 sound chip emulator
+Copyright (c) 2015 Petri Hakkinen
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include
+#include
+#include "soloud_vic.h"
+
+namespace SoLoud
+{
+
+ VicInstance::VicInstance(Vic *aParent)
+ {
+ m_parent = aParent;
+
+ for(int i = 0; i < 4; i++)
+ m_phase[i] = 0;
+
+ m_noisePos = 0;
+ }
+
+ VicInstance::~VicInstance()
+ {
+ }
+
+ unsigned int VicInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ unsigned int phaseAdder[4] = { 0, 0, 0, 0 };
+ for(int i = 0; i < 4; i++)
+ {
+ unsigned char reg = m_parent->getRegister(i);
+ if(reg >= 128)
+ {
+ float freq = m_parent->m_clocks[i] / (float)(reg < 255 ? 255 - reg : 1);
+ phaseAdder[i] = (unsigned int)(freq * 65536.0f / 44100.0f + 0.5f);
+ }
+ }
+
+ for(int i = 0; i < (signed)aSamplesToRead; i++)
+ {
+ float s = 0.0f;
+
+ // square waves
+ for(int v = 0; v < 3; v++)
+ {
+ if(phaseAdder[v] != 0)
+ {
+ s += (m_phase[v] < 32768 ? 0.5f : -0.5f);
+ m_phase[v] = (m_phase[v] + phaseAdder[v]) & 65535;
+ }
+ }
+
+ // noise
+ if(phaseAdder[3] != 0)
+ {
+ s += (float)m_parent->m_noise[m_noisePos] / 255.0f - 0.5f;
+
+ m_phase[3] += phaseAdder[3];
+
+ if(m_phase[3] >= 32768)
+ {
+ m_noisePos = (m_noisePos + 1) & 8191;
+ m_phase[3] &= 32767;
+ }
+ }
+
+ aBuffer[i] = s / 4.0f;
+ }
+ return aSamplesToRead;
+ }
+
+ bool VicInstance::hasEnded()
+ {
+ return false;
+ }
+
+ Vic::Vic()
+ {
+ mBaseSamplerate = 44100;
+ setModel(PAL);
+
+ for(int i = 0; i < MAX_REGS; i++)
+ m_regs[i] = 0;
+
+ // Galois LFSR (source: https://en.wikipedia.org/wiki/Linear_feedback_shift_register)
+ unsigned short lfsr = 0xACE1u;
+ for(int i = 0; i < 8192; i++)
+ {
+ unsigned lsb = lfsr & 1;
+ lfsr >>= 1;
+ lfsr ^= (unsigned)(-(signed)lsb) & 0xB400u;
+ m_noise[i] = (lfsr & 0xff) ^ (lfsr >> 8);
+ }
+ }
+
+ Vic::~Vic()
+ {
+ stop();
+ }
+
+ void Vic::setModel(int model)
+ {
+ m_model = model;
+
+ switch(model)
+ {
+ case PAL:
+ m_clocks[0] = 4329.0f;
+ m_clocks[1] = 8659.0f;
+ m_clocks[2] = 17320.0f;
+ m_clocks[3] = 34640.0f;
+ break;
+
+ case NTSC:
+ m_clocks[0] = 3995.0f;
+ m_clocks[1] = 7990.0f;
+ m_clocks[2] = 15980.0f;
+ m_clocks[3] = 31960.0f;
+ break;
+ }
+ }
+
+ int Vic::getModel() const
+ {
+ return m_model;
+ }
+
+ void Vic::setRegister(int reg, unsigned char value)
+ {
+ m_regs[reg] = value;
+ }
+
+ unsigned char Vic::getRegister(int reg)
+ {
+ return m_regs[reg];
+ }
+
+ AudioSourceInstance * Vic::createInstance()
+ {
+ return new VicInstance(this);
+ }
+
+};
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/vizsn/soloud_vizsn.cpp b/src/soloud/src/audiosource/vizsn/soloud_vizsn.cpp
new file mode 100644
index 0000000..5d654d2
--- /dev/null
+++ b/src/soloud/src/audiosource/vizsn/soloud_vizsn.cpp
@@ -0,0 +1,533 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+vizsn speech synthesizer (c) by Ville-Matias Heikkilä,
+released under WTFPL, http://www.wtfpl.net/txt/copying/
+(in short, "do whatever you want to")
+
+Integration and changes to work with SoLoud by Jari Komppa,
+released under same license.
+*/
+
+#include
+#include
+#include
+#include
+#include "soloud_vizsn.h"
+
+/*
+
+ lähtöfunktiot: voice, noise
+ muut: pitch
+ volyymit: voice, asp, fric, bypass (4kpl)
+ resonaattorit: lp, nz (ar), npc, 1p, 2p, 3p, 4p, 5p, 6p, out (10kpl)
+
+
+ [voice] [noise]
+ |
+ | |
+ | |
+ (re.LP) |
+ | |
+ (*voice) (*asp)
+ | |
+ `--[+]--'
+ |
+ (ar.NZ)
+ |
+ (re.NPC)
+ |
+ .-----------. [noise]
+ | | |
+(re.1P) (diff) (*fric)
+ | | |
+ | [+]------'
+ | |
+ [-]-(re.4P)--[
+ | |
+ [-]-(re.3P)--[ <-- selvitäänkö kahdella tuossa vaiheessa?
+ | |
+ [-]-(re.2P)--[
+ | |
+ | (*bypass)
+ | |
+ `----[-]----'
+ |'
+ (re.OUT)
+ |
+ out
+
+ resonaattori:
+
+ x = a*input + b*y + c*z
+
+ ja tulos kiertää x=>y=>z
+
+ antiresonaattori:
+
+ x = a*input + b*y + c*z
+
+ inputti kiertää i=>y=>z
+*/
+
+#define RLP 0
+#define RNZ 1
+#define RNPC 2
+#define ROUT 3
+#define R1P 4
+#define R2P 5
+#define R3P 6
+#define R4P 7
+#define R5P 8
+#define R6P 9
+#define P_A 0
+#define P_AE 1
+#define P_E 2
+#define P_OE 3
+#define P_O 4
+#define P_I 5
+#define P_Y 6
+#define P_U 7
+#define P_H 8
+#define P_V 9
+#define P_J 10
+#define P_S 11
+#define P_L 12
+#define P_R 13
+#define P_K 14
+#define P_T 15
+#define P_P 16
+#define P_N 17
+#define P_M 18
+#define P_NG 19
+#define P_NEW 12
+#define P_END -1
+#define P_CLR -2
+
+#define filter(i,v) i##last=i+(i##last*v);
+#define plosive(a) ((((a) >= P_K) && ((a) <= P_P)) || ((a) == P_CLR))
+
+static const float vowtab[8][4][2] =
+{
+ /* a */ 0.10f, 1.6f, 0, 0, 0.2f, 1.5f, 0, 0,
+ /* ä */ 0.10f, 1.6f, 0, 0, 0.2f, 0, 0, 0,
+
+ /* e */ 0.08f, 1.8f, 0, 0, 0.2f, -0.8f, 0, 0,
+ /* ö */ 0.08f, 1.8f, 0, 0, 0.3f, 0.9f, 0, 0,
+ /* o */ 0.08f, 1.8f, 0, 0, 0.2f, 1.6f, 0, 0,
+
+ /* i */ 0.05f, 1.9f, 0, 0, 0.2f, -1.5f, 0, 0,
+ /* y */ 0.05f, 1.9f, 0, 0, 0.2f, 0.8f, 0, 0,
+ /* u */ 0.05f, 1.9f, 0, 0, 0.1f, 1.7f, 0, 0
+};
+
+/* integerisoi (8bit): kerro kolmella */
+static const float voo[13][5 + 3 * 10] =
+{
+ /* frikatiivit & puolivokaalit */
+
+ /* h */
+ 0.0f, 0.0f, 0.36f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.9f, -0.92f, 1.17f, 0.8f, -0.02f,
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* v */
+ 0.7f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.9f, -0.92f, 1.17f, 0.8f, -0.02f,
+ 0.005f, 1.9f, -0.95f, 0.0f, 0.0f, 0.0f, 0.04f, 1.5f, -0.93f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* j */
+ 0.7f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.9f, -0.92f, 1.17f, 0.8f, -0.02f,
+ 0.005f, 1.9f, -0.95f, 0.0f, 0.0f, 0.0f, 0.04f, -1.5f, -0.93f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* s */
+ 0.0f, 0.0f, 0.36f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.9f, -0.92f, 1.7f, 0.1f, -0.02f,
+ 0.0f, 0.1f, -0.02f, 0.0f, 1.76f, -0.85f, 0.01f, 0.42f, -0.93f, 0.02f, -1.37f, -0.68f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* l */
+ 0.8f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.88f, -0.92f, 1.17f, 0.8f, -0.02f,
+ 0.1f, -0.5f, -0.93f, 0.6f, 1.0f, -0.93f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* r */
+ 0.8f, 0.4f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.9f, -0.92f, 1.17f, 0.8f, -0.02f,
+ -0.2f, 0.0f, -0.93f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* klusiilit */
+
+ /* k */
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.9f, -0.92f, 1.17f, 0.8f, -0.02f,
+ 0.0f, 1.95f, -0.94f, 0.4f, 1.0f, -0.93f, 0.6f, 1.0f, -0.89f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* t */
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.9f, -0.92f, 1.17f, 0.8f, -0.02f,
+ 0.0f, 1.6f, -0.94f, 0.0f, 0.3f, -0.93f, 1.5f, -0.6f, -0.89f, 1.8f, -1.5f, -0.68f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* p */
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 22.0f, -42.0f, 21.0f, 0.04f, 1.9f, -0.92f, 1.17f, 0.8f, -0.02f,
+ 0.01f, 1.9f, -0.94f, 1.5f, 1.7f, -0.93f, 1.0f, 1.0f, -0.89f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* nasaalit */
+
+ /* n */
+ 0.81f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 15.0f, -20.0f, 10.0f, 0.04f, 1.88f, -0.92f, 1.17f, 0.1f, -0.02f,
+ 0.02f, 1.83f, -0.97f, 0.10f, 0.26f, -0.83f, 0.06f, -0.85f, -0.82f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* m */
+ 0.81f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 15.0f, -20.0f, 10.0f, 0.04f, 1.88f, -0.92f, 1.17f, 0.1f, -0.02f,
+ 0.08f, 1.5f, -0.94f, 0.06f, 1.0f, -0.9f, 0.05f, 1.5f, -0.89f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+
+ /* ng */
+ 0.81f, 0.0f, 0.0f, 0.0f, 0.0f, 0.32f, 1.36f, -0.67f, 15.0f, -20.0f, 10.0f, 0.04f, 1.88f, -0.92f, 1.17f, 0.1f, -0.02f,
+ 0.1f, 1.6f, -0.94f, 0.7f, -1.0f, -0.91f, 0.1f, 1.16f, -0.91f, 0.03f, -1.3f, -0.68f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
+};
+
+static const char keyz[] =
+{
+ P_A, P_P, P_S, P_T, P_E, P_V, P_NG,
+ P_H, P_I, P_J, P_K, P_L, P_M, P_N,
+ P_O, P_P, P_K, P_R, P_S, P_T, P_U,
+ P_V, P_U, P_S, P_Y, P_S, P_AE, P_OE
+};
+
+namespace SoLoud
+{
+ float VizsnResonator::resonate(float i)
+ {
+ float x = (a * i) + (b * p1) + (c * p2);
+ p2 = p1;
+ p1 = x;
+ return x;
+ }
+
+ float VizsnResonator::antiresonate(float i)
+ {
+ float x = a * i + b * p1 + c * p2;
+ p2 = p1;
+ p1 = i;
+ return x;
+ }
+ VizsnInstance::VizsnInstance(Vizsn *aParent)
+ {
+ mParent = aParent;
+ mPtr = 0;
+ mCurrentVoiceType = 6;
+ memset(mEchobuf, 0, 1024 * sizeof(int));
+ mPitch = 800;
+ mS = mParent->mText;
+ mBufwrite = 0;
+ mBufread = 0;
+ mA = 0;
+ mB = 100;
+ mOrgv = -1;
+ mGlotlast = 0;
+ }
+
+ VizsnInstance::~VizsnInstance()
+ {
+ }
+
+ unsigned int VizsnInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ unsigned int idx = 0;
+ int i, j;
+ if (mBufwrite > mBufread)
+ {
+ for (; mBufwrite > mBufread && idx < aSamplesToRead; mBufread++)
+ {
+ aBuffer[idx] = mBuf[mBufread];
+ idx++;
+ }
+ }
+ if (idx == aSamplesToRead) return aSamplesToRead;
+ mBufwrite = mBufread = 0;
+ while (idx + mBufwrite < aSamplesToRead)
+ {
+ setphone(&mBank0, *mS, mPitch);
+
+ if (*mS == P_END)
+ {
+ mBuf[mBufwrite] = 0;
+ mBufwrite++;
+ SOLOUD_ASSERT(mBufwrite < 2048);
+ break;
+ }
+
+ setphone(&mBank1, mS[1], mPitch);//pitch+=50); -- RAISE SOUND
+
+ slidePrepare(50);
+
+ mNper = 0;
+
+ for (i = plosive(*mS) ? 100 : 300; i; i--)
+ {
+ mBuf[mBufwrite] = genwave();
+ mBufwrite++;
+ SOLOUD_ASSERT(mBufwrite < 2048);
+ }
+
+ if (!plosive(mS[1]))
+ {
+ for (i = 50; i; i--)
+ {
+ for (j = 10; j; j--)
+ {
+ mBuf[mBufwrite] = genwave();
+ mBufwrite++;
+ SOLOUD_ASSERT(mBufwrite < 2048);
+ }
+ slideTick();
+ }
+ }
+ else
+ {
+ for (i = 50; i; i--)
+ {
+ for (j = 3; j; j--)
+ {
+ mBuf[mBufwrite] = genwave();
+ mBufwrite++;
+ SOLOUD_ASSERT(mBufwrite < 2048);
+ }
+ }
+ }
+
+ mS++;
+
+ memcpy(&mBank0, &mBank1, sizeof(VizsnBank));
+ }
+ for (; idx < aSamplesToRead; idx++)
+ {
+ aBuffer[idx] = mBuf[mBufread];
+ mBufread++;
+ }
+ return aSamplesToRead;
+ }
+
+ float VizsnInstance::vcsrc(int aPitch, int aVoicetype)
+ {
+ mA += aPitch;
+
+ if (mOrgv != aVoicetype)
+ {
+ mOrgv = aVoicetype;
+ mA = 0;
+ mB = 100;
+ }
+
+ switch (aVoicetype)
+ {
+ case 0: return (mA & (256 + 128 + 32)) * 5 * 0.0002f;
+ case 1: return (float)(sin(mA * 0.0002) * cos(mA * 0.0003) * 0.2f + ((rand() % 200 - 100) / 300.0f)); // ilmava
+ case 2: return (float)tan(mA*0.00002)*0.01f; // burpy
+ case 3: return ((mA & 65535) > 32768 ? 65535 : 0) * 0.00001f; // square wave
+ case 4: return mA * mA * 0.0000000002f; // kuisku
+ case 5: mA += 3; mB++; return ((mA & 255) > ((mB >> 2) & 255)) ? 0.3f : 0.0f;
+ case 7: return ((mA >> 8) & (256 + 128)) * 0.001f; // robottipulssi
+ case 8: return (float)(rand() % (1 + ((mA & 65535) >> 8))) / 256; // -- hiukka ihmisempi tsaatana
+ case 9: return ((float)(rand() & 32767)) / 32767; // -- noise: tsaatana
+ case 6: // fallthrough
+ default: return (mA & 65535) * (0.001f / 256); /*-- sawtooth: near natural */
+ }
+ }
+
+ float VizsnInstance::noisrc()
+ {
+ return ((float)(rand() & 32767)) / 32768;
+ }
+
+ float VizsnInstance::genwave()
+ {
+ float s, o, noise, voice, glot, parglot;
+ int ob;
+
+ noise = noisrc();
+
+ if (mNper > mNmod)
+ {
+ noise *= 0.5f;
+ }
+
+ s = mBank0.frica * noise;
+
+ voice = vcsrc((int)floor(mBank0.pitch), mCurrentVoiceType);
+ voice = mBank0.r[RLP].resonate(voice);
+
+ if (mNper < mNopen)
+ {
+ voice += mBank0.breth * noise;
+ }
+
+ parglot = glot = (mBank0.voice * voice) + (mBank0.aspir * noise);
+
+ parglot = mBank0.r[RNZ].antiresonate(parglot);
+ parglot = mBank0.r[RNPC].resonate(parglot);
+ s += (parglot - mGlotlast);
+ mGlotlast = parglot;
+ o = mBank0.r[R1P].resonate(parglot);
+
+ int i;
+ for (i = R4P; i > R2P; i--)
+ {
+ o = mBank0.r[i].resonate(s) - o;
+ }
+
+ o = mBank0.r[ROUT].resonate(mBank0.bypas * s - o);
+
+ /*********/
+
+ ob = (int)floor(o * 400 * 256 + (mEchobuf[mPtr] / 4));
+ mEchobuf[mPtr] = ob;
+ mPtr = (mPtr + 1) & 1023;
+
+ ob = (ob >> 8) + 128;
+
+ if (ob < 0) ob = 0;
+ if (ob > 255) ob = 255;
+
+ mNper++;
+
+ return ob * (1.0f / 255.0f);
+ }
+
+ void VizsnInstance::setphone(VizsnBank *aB, char aP, float aPitch)
+ {
+ int i;
+ aB->frica = aB->aspir = aB->bypas = aB->breth = aB->voice = 0;
+ aB->pitch = mPitch;
+
+ if (aP < 0)
+ {
+ for (i = 0; i < 10; i++)
+ {
+ aB->r[i].p1 = aB->r[i].p2 = aB->r[i].a = aB->r[i].b = aB->r[i].c = 0;
+ }
+ }
+ else
+ {
+ if (aP < 8)
+ {
+ /* vokaali */
+ VizsnResonator *r = aB->r;
+ const float *s = vowtab[aP][0];
+
+ r[R1P].c = -0.95f; r[R2P].c = -0.93f; r[R3P].c = -0.88f; r[R4P].c = -0.67f;
+ r[RLP].a = 0.31f; r[RLP].b = 1.35f; r[RLP].c = -0.67f;
+ r[RNZ].a = 22.0f; r[RNZ].b = -42.0f; r[RNZ].c = 21.0f;
+ r[RNPC].a = 0.04f; r[RNPC].b = 1.87f; r[RNPC].c = -0.92f;
+ r[ROUT].a = 1.16f; r[ROUT].b = 0.08f; r[ROUT].c = -0.02f;
+
+ r += R1P;
+
+ for (i = 4; i; i--)
+ {
+ r->a = *s++;
+ r->b = *s++;
+ r++;
+ }
+
+ aB->voice = 0.8f;
+ }
+ else
+ {
+ /* v */
+ int i;
+ const float *v = voo[aP - 8];
+
+ aB->voice = *v++;
+ aB->aspir = *v++;
+ aB->frica = *v++;
+ aB->bypas = *v++;
+ aB->breth = *v++;
+
+ for (i = 0; i < 10; i++)
+ {
+ aB->r[i].a = *v++;
+ aB->r[i].b = *v++;
+ aB->r[i].c = *v++;
+ }
+
+ aB->voice = 0.8f;
+
+ aB->breth = 0.18f;
+ }
+ }
+ }
+
+ void VizsnInstance::slidePrepare(int aNumtix)
+ {
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ mBank0to1.r[i].a = (mBank1.r[i].a - mBank0.r[i].a) / aNumtix;
+ mBank0to1.r[i].b = (mBank1.r[i].b - mBank0.r[i].b) / aNumtix;
+ mBank0to1.r[i].c = (mBank1.r[i].c - mBank0.r[i].c) / aNumtix;
+ }
+
+ mBank0to1.pitch = (mBank1.pitch - mBank0.pitch) / aNumtix;
+ }
+
+ void VizsnInstance::slideTick()
+ {
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ mBank0.r[i].a += mBank0to1.r[i].a;
+ mBank0.r[i].b += mBank0to1.r[i].b;
+ mBank0.r[i].c += mBank0to1.r[i].c;
+ }
+
+ mBank0.pitch += mBank0to1.pitch;
+ }
+
+ bool VizsnInstance::hasEnded()
+ {
+ return *mS == P_END;
+ }
+
+ Vizsn::Vizsn()
+ {
+ mBaseSamplerate = 8000;
+ mText = 0;
+ }
+
+ Vizsn::~Vizsn()
+ {
+ stop();
+ }
+
+ AudioSourceInstance * Vizsn::createInstance()
+ {
+ return new VizsnInstance(this);
+ }
+
+ void Vizsn::setText(char *aText)
+ {
+ if (!aText)
+ return;
+ stop();
+ delete[] mText;
+ int len = (int)strlen(aText);
+ mText = new char[len + 3];
+ memcpy(mText+1, aText, len);
+ mText[0] = P_CLR;
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ int c = mText[i + 1];
+ if (c == '\x84' || c == -124) c = '{'; // ä
+ if (c == '\x94' || c == -108) c = '|'; // ö
+ if (c >= 'a' && c <= '|')
+ {
+ mText[i + 1] = keyz[c - 'a'];
+ }
+ else
+ {
+ mText[i + 1] = P_CLR;
+ }
+ }
+ mText[len + 1] = P_END;
+ mText[len + 2] = 0;
+ }
+
+};
diff --git a/src/soloud/src/audiosource/wav/dr_flac.h b/src/soloud/src/audiosource/wav/dr_flac.h
new file mode 100644
index 0000000..fab4212
--- /dev/null
+++ b/src/soloud/src/audiosource/wav/dr_flac.h
@@ -0,0 +1,8154 @@
+// FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file.
+// dr_flac - v0.10.0 - 2018-09-11
+//
+// David Reid - mackron@gmail.com
+
+// USAGE
+//
+// dr_flac is a single-file library. To use it, do something like the following in one .c file.
+// #define DR_FLAC_IMPLEMENTATION
+// #include "dr_flac.h"
+//
+// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data,
+// do something like the following:
+//
+// drflac* pFlac = drflac_open_file("MySong.flac");
+// if (pFlac == NULL) {
+// // Failed to open FLAC file
+// }
+//
+// drflac_int32* pSamples = malloc(pFlac->totalSampleCount * sizeof(drflac_int32));
+// drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalSampleCount, pSamples);
+//
+// The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of
+// channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are
+// always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has
+// seamless support for Ogg encapsulated FLAC streams as well.
+//
+// You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and
+// the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of
+// samples, just call it again. Example:
+//
+// while (drflac_read_pcm_frames_s32(pFlac, chunkSize, pChunkSamples) > 0) {
+// do_something();
+// }
+//
+// You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example,
+// if you were to seek to the sample at index 0 in a stereo stream, you'll be seeking to the first sample of the left channel.
+// The sample at index 1 will be the first sample of the right channel. The sample at index 2 will be the second sample of the
+// left channel, etc.
+//
+//
+// If you just want to quickly decode an entire FLAC file in one go you can do something like this:
+//
+// unsigned int channels;
+// unsigned int sampleRate;
+// drflac_uint64 totalSampleCount;
+// drflac_int32* pSampleData = drflac_open_and_decode_file_s32("MySong.flac", &channels, &sampleRate, &totalSampleCount);
+// if (pSampleData == NULL) {
+// // Failed to open and decode FLAC file.
+// }
+//
+// ...
+//
+// drflac_free(pSampleData);
+//
+//
+// You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs
+// respectively, but note that these should be considered lossy.
+//
+//
+// If you need access to metadata (album art, etc.), use drflac_open_with_metadata(), drflac_open_file_with_metdata() or
+// drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the
+// normal versions and also just a little bit harder to use.
+//
+// dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before
+// drflac_open_with_metdata() returns.
+//
+//
+// The main opening APIs (drflac_open(), etc.) will fail if the header is not present. The presents a problem in certain
+// scenarios such as broadcast style streams like internet radio where the header may not be present because the user has
+// started playback mid-stream. To handle this, use the relaxed APIs: drflac_open_relaxed() and drflac_open_with_metadata_relaxed().
+//
+// It is not recommended to use these APIs for file based streams because a missing header would usually indicate a
+// corrupted or perverse file. In addition, these APIs can take a long time to initialize because they may need to spend
+// a lot of time finding the first frame.
+//
+//
+//
+// OPTIONS
+// #define these options before including this file.
+//
+// #define DR_FLAC_NO_STDIO
+// Disable drflac_open_file() and family.
+//
+// #define DR_FLAC_NO_OGG
+// Disables support for Ogg/FLAC streams.
+//
+// #define DR_FLAC_BUFFER_SIZE
+// Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls
+// back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing
+// returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of
+// onRead(), or increase it if it's very inefficient. Must be a multiple of 8.
+//
+// #define DR_FLAC_NO_CRC
+// Disables CRC checks. This will offer a performance boost when CRC is unnecessary.
+//
+// #define DR_FLAC_NO_SIMD
+// Disables SIMD optimizations (SSE on x86/x64 architectures). Use this if you are having compatibility issues with your
+// compiler.
+//
+//
+//
+// QUICK NOTES
+// - dr_flac does not currently support changing the sample rate nor channel count mid stream.
+// - Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as.
+// - This has not been tested on big-endian architectures.
+// - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization.
+// - When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open()
+// returning inconsistent samples.
+
+#ifndef dr_flac_h
+#define dr_flac_h
+
+#include
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+typedef signed char drflac_int8;
+typedef unsigned char drflac_uint8;
+typedef signed short drflac_int16;
+typedef unsigned short drflac_uint16;
+typedef signed int drflac_int32;
+typedef unsigned int drflac_uint32;
+typedef signed __int64 drflac_int64;
+typedef unsigned __int64 drflac_uint64;
+#else
+#include
+typedef int8_t drflac_int8;
+typedef uint8_t drflac_uint8;
+typedef int16_t drflac_int16;
+typedef uint16_t drflac_uint16;
+typedef int32_t drflac_int32;
+typedef uint32_t drflac_uint32;
+typedef int64_t drflac_int64;
+typedef uint64_t drflac_uint64;
+#endif
+typedef drflac_uint8 drflac_bool8;
+typedef drflac_uint32 drflac_bool32;
+#define DRFLAC_TRUE 1
+#define DRFLAC_FALSE 0
+
+#if defined(_MSC_VER) && _MSC_VER >= 1700 // Visual Studio 2012
+#define DRFLAC_DEPRECATED __declspec(deprecated)
+#elif (defined(__GNUC__) && __GNUC__ >= 4)
+#define DRFLAC_DEPRECATED __attribute__((deprecated))
+#elif (defined(__clang__) && __has_feature(attribute_deprecated))
+#define DRFLAC_DEPRECATED __attribute__((deprecated))
+#else
+#define DRFLAC_DEPRECATED
+#endif
+
+// As data is read from the client it is placed into an internal buffer for fast access. This controls the
+// size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing
+// returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8.
+#ifndef DR_FLAC_BUFFER_SIZE
+#define DR_FLAC_BUFFER_SIZE 4096
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Check if we can enable 64-bit optimizations.
+#if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+#define DRFLAC_64BIT
+#endif
+
+#ifdef DRFLAC_64BIT
+typedef drflac_uint64 drflac_cache_t;
+#else
+typedef drflac_uint32 drflac_cache_t;
+#endif
+
+// The various metadata block types.
+#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
+#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1
+#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2
+#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
+#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
+#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5
+#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6
+#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127
+
+// The various picture types specified in the PICTURE block.
+#define DRFLAC_PICTURE_TYPE_OTHER 0
+#define DRFLAC_PICTURE_TYPE_FILE_ICON 1
+#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
+#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3
+#define DRFLAC_PICTURE_TYPE_COVER_BACK 4
+#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5
+#define DRFLAC_PICTURE_TYPE_MEDIA 6
+#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7
+#define DRFLAC_PICTURE_TYPE_ARTIST 8
+#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9
+#define DRFLAC_PICTURE_TYPE_BAND 10
+#define DRFLAC_PICTURE_TYPE_COMPOSER 11
+#define DRFLAC_PICTURE_TYPE_LYRICIST 12
+#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13
+#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14
+#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
+#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
+#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
+#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18
+#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
+#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
+
+typedef enum
+{
+ drflac_container_native,
+ drflac_container_ogg,
+ drflac_container_unknown
+} drflac_container;
+
+typedef enum
+{
+ drflac_seek_origin_start,
+ drflac_seek_origin_current
+} drflac_seek_origin;
+
+// Packing is important on this structure because we map this directly to the raw data within the SEEKTABLE metadata block.
+#pragma pack(2)
+typedef struct
+{
+ drflac_uint64 firstSample;
+ drflac_uint64 frameOffset; // The offset from the first byte of the header of the first frame.
+ drflac_uint16 sampleCount;
+} drflac_seekpoint;
+#pragma pack()
+
+typedef struct
+{
+ drflac_uint16 minBlockSize;
+ drflac_uint16 maxBlockSize;
+ drflac_uint32 minFrameSize;
+ drflac_uint32 maxFrameSize;
+ drflac_uint32 sampleRate;
+ drflac_uint8 channels;
+ drflac_uint8 bitsPerSample;
+ drflac_uint64 totalSampleCount;
+ drflac_uint8 md5[16];
+} drflac_streaminfo;
+
+typedef struct
+{
+ // The metadata type. Use this to know how to interpret the data below.
+ drflac_uint32 type;
+
+ // A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to
+ // not modify the contents of this buffer. Use the structures below for more meaningful and structured
+ // information about the metadata. It's possible for this to be null.
+ const void* pRawData;
+
+ // The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL.
+ drflac_uint32 rawDataSize;
+
+ union
+ {
+ drflac_streaminfo streaminfo;
+
+ struct
+ {
+ int unused;
+ } padding;
+
+ struct
+ {
+ drflac_uint32 id;
+ const void* pData;
+ drflac_uint32 dataSize;
+ } application;
+
+ struct
+ {
+ drflac_uint32 seekpointCount;
+ const drflac_seekpoint* pSeekpoints;
+ } seektable;
+
+ struct
+ {
+ drflac_uint32 vendorLength;
+ const char* vendor;
+ drflac_uint32 commentCount;
+ const void* pComments;
+ } vorbis_comment;
+
+ struct
+ {
+ char catalog[128];
+ drflac_uint64 leadInSampleCount;
+ drflac_bool32 isCD;
+ drflac_uint8 trackCount;
+ const void* pTrackData;
+ } cuesheet;
+
+ struct
+ {
+ drflac_uint32 type;
+ drflac_uint32 mimeLength;
+ const char* mime;
+ drflac_uint32 descriptionLength;
+ const char* description;
+ drflac_uint32 width;
+ drflac_uint32 height;
+ drflac_uint32 colorDepth;
+ drflac_uint32 indexColorCount;
+ drflac_uint32 pictureDataSize;
+ const drflac_uint8* pPictureData;
+ } picture;
+ } data;
+} drflac_metadata;
+
+
+// Callback for when data needs to be read from the client.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// pBufferOut [out] The output buffer.
+// bytesToRead [in] The number of bytes to read.
+//
+// Returns the number of bytes actually read.
+//
+// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+// either the entire bytesToRead is filled or you have reached the end of the stream.
+typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+// Callback for when data needs to be seeked.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
+// origin [in] The origin of the seek - the current position or the start of the stream.
+//
+// Returns whether or not the seek was successful.
+//
+// The offset will never be negative. Whether or not it is relative to the beginning or current position is determined
+// by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current.
+typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
+
+// Callback for when a metadata block is read.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// pMetadata [in] A pointer to a structure containing the data of the metadata block.
+//
+// Use pMetadata->type to determine which metadata block is being handled and how to read the data.
+typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata);
+
+
+// Structure for internal use. Only used for decoders opened with drflac_open_memory.
+typedef struct
+{
+ const drflac_uint8* data;
+ size_t dataSize;
+ size_t currentReadPos;
+} drflac__memory_stream;
+
+// Structure for internal use. Used for bit streaming.
+typedef struct
+{
+ // The function to call when more data needs to be read.
+ drflac_read_proc onRead;
+
+ // The function to call when the current read position needs to be moved.
+ drflac_seek_proc onSeek;
+
+ // The user data to pass around to onRead and onSeek.
+ void* pUserData;
+
+
+ // The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the
+ // stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether
+ // or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t).
+ size_t unalignedByteCount;
+
+ // The content of the unaligned bytes.
+ drflac_cache_t unalignedCache;
+
+ // The index of the next valid cache line in the "L2" cache.
+ drflac_uint32 nextL2Line;
+
+ // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining.
+ drflac_uint32 consumedBits;
+
+ // The cached data which was most recently read from the client. There are two levels of cache. Data flows as such:
+ // Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions.
+ drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)];
+ drflac_cache_t cache;
+
+ // CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this
+ // is reset to 0 at the beginning of each frame.
+ drflac_uint16 crc16;
+ drflac_cache_t crc16Cache; // A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded.
+ drflac_uint32 crc16CacheIgnoredBytes; // The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache.
+} drflac_bs;
+
+typedef struct
+{
+ // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC.
+ drflac_uint8 subframeType;
+
+ // The number of wasted bits per sample as specified by the sub-frame header.
+ drflac_uint8 wastedBitsPerSample;
+
+ // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC.
+ drflac_uint8 lpcOrder;
+
+ // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because
+ // an extra bit is required for side channels when interchannel decorrelation is being used.
+ drflac_uint32 bitsPerSample;
+
+ // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that
+ // it's a signed 32-bit integer for each value.
+ drflac_int32* pDecodedSamples;
+} drflac_subframe;
+
+typedef struct
+{
+ // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will
+ // always be set to 0.
+ drflac_uint64 sampleNumber;
+
+ // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0.
+ drflac_uint32 frameNumber;
+
+ // The sample rate of this frame.
+ drflac_uint32 sampleRate;
+
+ // The number of samples in each sub-frame within this frame.
+ drflac_uint16 blockSize;
+
+ // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this
+ // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE.
+ drflac_uint8 channelAssignment;
+
+ // The number of bits per sample within this frame.
+ drflac_uint8 bitsPerSample;
+
+ // The frame's CRC.
+ drflac_uint8 crc8;
+} drflac_frame_header;
+
+typedef struct
+{
+ // The header.
+ drflac_frame_header header;
+
+ // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples
+ // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame.
+ drflac_uint32 samplesRemaining;
+
+ // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels.
+ drflac_subframe subframes[8];
+} drflac_frame;
+
+typedef struct
+{
+ // The function to call when a metadata block is read.
+ drflac_meta_proc onMeta;
+
+ // The user data posted to the metadata callback function.
+ void* pUserDataMD;
+
+
+ // The sample rate. Will be set to something like 44100.
+ drflac_uint32 sampleRate;
+
+ // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the
+ // value specified in the STREAMINFO block.
+ drflac_uint8 channels;
+
+ // The bits per sample. Will be set to something like 16, 24, etc.
+ drflac_uint8 bitsPerSample;
+
+ // The maximum block size, in samples. This number represents the number of samples in each channel (not combined).
+ drflac_uint16 maxBlockSize;
+
+ // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels,
+ // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a
+ // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio.
+ drflac_uint64 totalSampleCount;
+ drflac_uint64 totalPCMFrameCount; // <-- Equal to totalSampleCount / channels.
+
+
+ // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream.
+ drflac_container container;
+
+ // The number of seekpoints in the seektable.
+ drflac_uint32 seekpointCount;
+
+
+ // Information about the frame the decoder is currently sitting on.
+ drflac_frame currentFrame;
+
+ // The index of the sample the decoder is currently sitting on. This is only used for seeking.
+ drflac_uint64 currentSample;
+
+ // The position of the first frame in the stream. This is only ever used for seeking.
+ drflac_uint64 firstFramePos;
+
+
+ // A hack to avoid a malloc() when opening a decoder with drflac_open_memory().
+ drflac__memory_stream memoryStream;
+
+
+ // A pointer to the decoded sample data. This is an offset of pExtraData.
+ drflac_int32* pDecodedSamples;
+
+ // A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table.
+ drflac_seekpoint* pSeekpoints;
+
+ // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData.
+ void* _oggbs;
+
+ // The bit streamer. The raw FLAC data is fed through this object.
+ drflac_bs bs;
+
+ // Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs.
+ drflac_uint8 pExtraData[1];
+} drflac;
+
+
+// Opens a FLAC decoder.
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated
+// FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with
+// multiplexed streams which basically means it can play FLAC encoded audio tracks in videos.
+//
+// This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where
+// the header may not be present.
+//
+// See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close()
+drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData);
+
+// The same as drflac_open(), except attempts to open the stream even when a header block is not present.
+//
+// Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do
+// not set this to drflac_container_unknown - that is for internal use only.
+//
+// Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never
+// found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an
+// indicator that the end of the stream was found.
+drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData);
+
+// Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.).
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// onMeta [in] The function to call for every metadata block.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC()
+// and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks.
+//
+// The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function
+// returns.
+//
+// The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC
+// stream where the header may not be present.
+//
+// Note that this will behave inconsistently with drflac_open() if the stream is an Ogg encapsulated stream and a metadata
+// block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When drflac_open_with_metadata()
+// is being used, the open routine will try to read the contents of the metadata block, whereas drflac_open() will simply
+// seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on
+// whether or not the stream is being opened with metadata.
+//
+// See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close()
+drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData);
+
+// The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present.
+//
+// See also: drflac_open_with_metadata(), drflac_open_relaxed()
+drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData);
+
+// Closes the given FLAC decoder.
+//
+// pFlac [in] The decoder to close.
+//
+// This will destroy the decoder object.
+void drflac_close(drflac* pFlac);
+
+
+// Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM.
+//
+// pFlac [in] The decoder.
+// framesToRead [in] The number of PCM frames to read.
+// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
+//
+// Returns the number of PCM frames actually read.
+//
+// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of frames
+// seeked.
+drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut);
+
+// Same as drflac_read_pcm_frames_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit.
+//
+// Note that this is lossy for streams where the bits per sample is larger than 16.
+drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut);
+
+// Same as drflac_read_pcm_frames_s32(), except outputs samples as 32-bit floating-point PCM.
+//
+// Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly
+// represent every possible number.
+drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut);
+
+// Seeks to the PCM frame at the given index.
+//
+// pFlac [in] The decoder.
+// pcmFrameIndex [in] The index of the PCM frame to seek to. See notes below.
+//
+// Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise.
+drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex);
+
+
+
+#ifndef DR_FLAC_NO_STDIO
+// Opens a FLAC decoder from the file at the given path.
+//
+// filename [in] The path of the file to open, either absolute or relative to the current directory.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the
+// number of files a process can have open at any given time, so keep this mind if you have many decoders open at the
+// same time.
+//
+// See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close()
+drflac* drflac_open_file(const char* filename);
+
+// Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.)
+//
+// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData);
+#endif
+
+// Opens a FLAC decoder from a pre-allocated block of memory
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the decoder.
+drflac* drflac_open_memory(const void* data, size_t dataSize);
+
+// Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.)
+//
+// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData);
+
+
+
+//// High Level APIs ////
+
+// Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a
+// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE().
+//
+// Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously
+// read samples into a dynamically sized buffer on the heap until no samples are left.
+//
+// Do not call this function on a broadcast type of stream (like internet radio streams and whatnot).
+drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+
+// Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples.
+drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+
+// Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples.
+float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+
+#ifndef DR_FLAC_NO_STDIO
+// Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file.
+drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+
+// Same as drflac_open_file_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples.
+drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+
+// Same as drflac_open_file_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples.
+float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+#endif
+
+// Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a block of memory.
+drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+
+// Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples.
+drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+
+// Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples.
+float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount);
+
+// Frees memory that was allocated internally by dr_flac.
+void drflac_free(void* p);
+
+
+// Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block.
+typedef struct
+{
+ drflac_uint32 countRemaining;
+ const char* pRunningData;
+} drflac_vorbis_comment_iterator;
+
+// Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT
+// metadata block.
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments);
+
+// Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The
+// returned string is NOT null terminated.
+const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut);
+
+
+// Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block.
+typedef struct
+{
+ drflac_uint32 countRemaining;
+ const char* pRunningData;
+} drflac_cuesheet_track_iterator;
+
+// Packing is important on this structure because we map this directly to the raw data within the CUESHEET metadata block.
+#pragma pack(4)
+typedef struct
+{
+ drflac_uint64 offset;
+ drflac_uint8 index;
+ drflac_uint8 reserved[3];
+} drflac_cuesheet_track_index;
+#pragma pack()
+
+typedef struct
+{
+ drflac_uint64 offset;
+ drflac_uint8 trackNumber;
+ char ISRC[12];
+ drflac_bool8 isAudio;
+ drflac_bool8 preEmphasis;
+ drflac_uint8 indexCount;
+ const drflac_cuesheet_track_index* pIndexPoints;
+} drflac_cuesheet_track;
+
+// Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata
+// block.
+void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData);
+
+// Goes to the next cuesheet track in the given iterator. If DRFLAC_FALSE is returned it means there are no more comments.
+drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack);
+
+
+//// Deprecated APIs ////
+DRFLAC_DEPRECATED drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* pBufferOut); // Use drflac_read_pcm_frames_s32() instead.
+DRFLAC_DEPRECATED drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut); // Use drflac_read_pcm_frames_s16() instead.
+DRFLAC_DEPRECATED drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut); // Use drflac_read_pcm_frames_f32() instead.
+DRFLAC_DEPRECATED drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex); // Use drflac_seek_to_pcm_frame() instead.
+DRFLAC_DEPRECATED drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_and_read_pcm_frames_s32().
+DRFLAC_DEPRECATED drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_and_read_pcm_frames_s16().
+DRFLAC_DEPRECATED float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_and_read_pcm_frames_f32().
+DRFLAC_DEPRECATED drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_file_and_read_pcm_frames_s32().
+DRFLAC_DEPRECATED drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_file_and_read_pcm_frames_s16().
+DRFLAC_DEPRECATED float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_file_and_read_pcm_frames_f32().
+DRFLAC_DEPRECATED drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_memory_and_read_pcm_frames_s32().
+DRFLAC_DEPRECATED drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_memory_and_read_pcm_frames_s16().
+DRFLAC_DEPRECATED float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); // Use drflac_open_memory_and_read_pcm_frames_f32().
+
+#ifdef __cplusplus
+}
+#endif
+#endif //dr_flac_h
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// IMPLEMENTATION
+//
+///////////////////////////////////////////////////////////////////////////////
+#ifdef DR_FLAC_IMPLEMENTATION
+#ifdef __linux__
+ #ifndef _BSD_SOURCE
+ #define _BSD_SOURCE
+ #endif
+ #ifndef __USE_BSD
+ #define __USE_BSD
+ #endif
+ #include
+#endif
+
+#include
+#include
+
+#ifdef _MSC_VER
+#define DRFLAC_INLINE __forceinline
+#else
+#ifdef __GNUC__
+#define DRFLAC_INLINE inline __attribute__((always_inline))
+#else
+#define DRFLAC_INLINE inline
+#endif
+#endif
+
+// CPU architecture.
+#if defined(__x86_64__) || defined(_M_X64)
+ #define DRFLAC_X64
+#elif defined(__i386) || defined(_M_IX86)
+ #define DRFLAC_X86
+#elif defined(__arm__) || defined(_M_ARM)
+ #define DRFLAC_ARM
+#endif
+
+// Intrinsics Support
+#if !defined(DR_FLAC_NO_SIMD)
+ #if defined(DRFLAC_X64) || defined(DRFLAC_X86)
+ #if defined(_MSC_VER) && !defined(__clang__)
+ // MSVC.
+ #if !defined(DRFLAC_NO_SSE2) // Assume all MSVC compilers support SSE2 intrinsics.
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) // 2010
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #else
+ // Assume GNUC-style.
+ #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2)
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41)
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #endif
+
+ // If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include.
+ #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
+ #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include()
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include()
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #endif
+
+ #if defined(DRFLAC_SUPPORT_SSE41)
+ #include
+ #elif defined(DRFLAC_SUPPORT_SSE2)
+ #include
+ #endif
+ #endif
+
+ #if defined(DRFLAC_ARM)
+ #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
+ #define DRFLAC_SUPPORT_NEON
+ #endif
+
+ // Fall back to looking for the #include file.
+ #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
+ #if !defined(DRFLAC_SUPPORT_NEON) && !defined(DRFLAC_NO_NEON) && __has_include()
+ #define DRFLAC_SUPPORT_NEON
+ #endif
+ #endif
+
+ #if defined(DRFLAC_SUPPORT_NEON)
+ #include
+ #endif
+ #endif
+#endif
+
+// Compile-time CPU feature support.
+#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
+ #if defined(_MSC_VER) && !defined(__clang__)
+ #if _MSC_VER >= 1400
+ #include
+ static void drflac__cpuid(int info[4], int fid)
+ {
+ __cpuid(info, fid);
+ }
+ #else
+ #define DRFLAC_NO_CPUID
+ #endif
+ #else
+ #if defined(__GNUC__) || defined(__clang__)
+ static void drflac__cpuid(int info[4], int fid)
+ {
+ // It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the
+ // specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for
+ // supporting different assembly dialects.
+ //
+ // What's basically happening is that we're saving and restoring the ebx register manually.
+ #if defined(DRFLAC_X86) && defined(__PIC__)
+ __asm__ __volatile__ (
+ "xchg{l} {%%}ebx, %k1;"
+ "cpuid;"
+ "xchg{l} {%%}ebx, %k1;"
+ : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #else
+ __asm__ __volatile__ (
+ "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #endif
+ }
+ #else
+ #define DRFLAC_NO_CPUID
+ #endif
+ #endif
+#else
+ #define DRFLAC_NO_CPUID
+#endif
+
+static DRFLAC_INLINE drflac_bool32 drflac_has_sse2()
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2)
+ #if defined(DRFLAC_X64)
+ return DRFLAC_TRUE; // 64-bit targets always support SSE2.
+ #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
+ return DRFLAC_TRUE; // If the compiler is allowed to freely generate SSE2 code we can assume support.
+ #else
+ #if defined(DRFLAC_NO_CPUID)
+ return DRFLAC_FALSE;
+ #else
+ int info[4];
+ drflac_cpuid(info, 1);
+ return (info[3] & (1 << 26)) != 0;
+ #endif
+ #endif
+ #else
+ return DRFLAC_FALSE; // SSE2 is only supported on x86 and x64 architectures.
+ #endif
+#else
+ return DRFLAC_FALSE; // No compiler support.
+#endif
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac_has_sse41()
+{
+#if defined(DRFLAC_SUPPORT_SSE41)
+ #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41)
+ #if defined(DRFLAC_X64)
+ return DRFLAC_TRUE; // 64-bit targets always support SSE4.1.
+ #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__)
+ return DRFLAC_TRUE; // If the compiler is allowed to freely generate SSE41 code we can assume support.
+ #else
+ #if defined(DRFLAC_NO_CPUID)
+ return DRFLAC_FALSE;
+ #else
+ int info[4];
+ drflac_cpuid(info, 1);
+ return (info[2] & (1 << 19)) != 0;
+ #endif
+ #endif
+ #else
+ return DRFLAC_FALSE; // SSE41 is only supported on x86 and x64 architectures.
+ #endif
+#else
+ return DRFLAC_FALSE; // No compiler support.
+#endif
+}
+
+
+#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
+#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
+#elif defined(__clang__)
+ #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
+ #endif
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1300
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+#elif defined(__clang__)
+ #if __has_builtin(__builtin_bswap16)
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap32)
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap64)
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #endif
+#elif defined(__GNUC__)
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #endif
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #endif
+#endif
+
+
+// Standard library stuff.
+#ifndef DRFLAC_ASSERT
+#include
+#define DRFLAC_ASSERT(expression) assert(expression)
+#endif
+#ifndef DRFLAC_MALLOC
+#define DRFLAC_MALLOC(sz) malloc((sz))
+#endif
+#ifndef DRFLAC_REALLOC
+#define DRFLAC_REALLOC(p, sz) realloc((p), (sz))
+#endif
+#ifndef DRFLAC_FREE
+#define DRFLAC_FREE(p) free((p))
+#endif
+#ifndef DRFLAC_COPY_MEMORY
+#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
+#endif
+#ifndef DRFLAC_ZERO_MEMORY
+#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
+#endif
+
+#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future.
+
+typedef drflac_int32 drflac_result;
+#define DRFLAC_SUCCESS 0
+#define DRFLAC_ERROR -1 // A generic error.
+#define DRFLAC_INVALID_ARGS -2
+#define DRFLAC_END_OF_STREAM -128
+#define DRFLAC_CRC_MISMATCH -129
+
+#define DRFLAC_SUBFRAME_CONSTANT 0
+#define DRFLAC_SUBFRAME_VERBATIM 1
+#define DRFLAC_SUBFRAME_FIXED 8
+#define DRFLAC_SUBFRAME_LPC 32
+#define DRFLAC_SUBFRAME_RESERVED 255
+
+#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
+#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
+
+#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
+#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
+#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
+#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
+
+// Keeps track of the number of leading samples for each sub-frame. This is required because the SSE pipeline will occasionally
+// reference excess prior samples.
+#define DRFLAC_LEADING_SAMPLES 32
+
+
+#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
+#define drflac_assert DRFLAC_ASSERT
+#define drflac_copy_memory DRFLAC_COPY_MEMORY
+#define drflac_zero_memory DRFLAC_ZERO_MEMORY
+
+
+// CPU caps.
+static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
+#ifndef DRFLAC_NO_CPUID
+static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE;
+static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE;
+static void drflac__init_cpu_caps()
+{
+ int info[4] = {0};
+
+ // LZCNT
+ drflac__cpuid(info, 0x80000001);
+ drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
+
+ // SSE2
+ drflac__gIsSSE2Supported = drflac_has_sse2();
+
+ // SSE4.1
+ drflac__gIsSSE41Supported = drflac_has_sse41();
+}
+#endif
+
+
+//// Endian Management ////
+static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian()
+{
+#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
+ return DRFLAC_TRUE;
+#else
+ int n = 1;
+ return (*(char*)&n) == 1;
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
+{
+#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #if defined(_MSC_VER)
+ return _byteswap_ushort(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap16(n);
+ #else
+ #error "This compiler does not support the byte swap intrinsic."
+ #endif
+#else
+ return ((n & 0xFF00) >> 8) |
+ ((n & 0x00FF) << 8);
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
+{
+#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #if defined(_MSC_VER)
+ return _byteswap_ulong(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap32(n);
+ #else
+ #error "This compiler does not support the byte swap intrinsic."
+ #endif
+#else
+ return ((n & 0xFF000000) >> 24) |
+ ((n & 0x00FF0000) >> 8) |
+ ((n & 0x0000FF00) << 8) |
+ ((n & 0x000000FF) << 24);
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
+{
+#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #if defined(_MSC_VER)
+ return _byteswap_uint64(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap64(n);
+ #else
+ #error "This compiler does not support the byte swap intrinsic."
+ #endif
+#else
+ return ((n & (drflac_uint64)0xFF00000000000000) >> 56) |
+ ((n & (drflac_uint64)0x00FF000000000000) >> 40) |
+ ((n & (drflac_uint64)0x0000FF0000000000) >> 24) |
+ ((n & (drflac_uint64)0x000000FF00000000) >> 8) |
+ ((n & (drflac_uint64)0x00000000FF000000) << 8) |
+ ((n & (drflac_uint64)0x0000000000FF0000) << 24) |
+ ((n & (drflac_uint64)0x000000000000FF00) << 40) |
+ ((n & (drflac_uint64)0x00000000000000FF) << 56);
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n)
+{
+#ifdef __linux__
+ return be16toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint16(n);
+ }
+
+ return n;
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n)
+{
+#ifdef __linux__
+ return be32toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint32(n);
+ }
+
+ return n;
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n)
+{
+#ifdef __linux__
+ return be64toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint64(n);
+ }
+
+ return n;
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n)
+{
+#ifdef __linux__
+ return le32toh(n);
+#else
+ if (!drflac__is_little_endian()) {
+ return drflac__swap_endian_uint32(n);
+ }
+
+ return n;
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n)
+{
+ drflac_uint32 result = 0;
+ result |= (n & 0x7F000000) >> 3;
+ result |= (n & 0x007F0000) >> 2;
+ result |= (n & 0x00007F00) >> 1;
+ result |= (n & 0x0000007F) >> 0;
+
+ return result;
+}
+
+
+
+// The CRC code below is based on this document: http://zlib.net/crc_v3.txt
+static drflac_uint8 drflac__crc8_table[] = {
+ 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
+ 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
+ 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
+ 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
+ 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
+ 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
+ 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
+ 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
+ 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
+ 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
+ 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
+ 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+ 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
+ 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
+ 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
+ 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
+};
+
+static drflac_uint16 drflac__crc16_table[] = {
+ 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
+ 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
+ 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
+ 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
+ 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
+ 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
+ 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
+ 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
+ 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
+ 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
+ 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
+ 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
+ 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
+ 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
+ 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
+ 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
+ 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
+ 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
+ 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
+ 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
+ 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
+ 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
+ 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
+ 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
+ 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
+ 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
+ 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
+ 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
+ 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
+ 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
+ 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
+ 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
+};
+
+static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data)
+{
+ return drflac__crc8_table[crc ^ data];
+}
+
+static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count)
+{
+ drflac_assert(count <= 32);
+
+#ifdef DR_FLAC_NO_CRC
+ (void)crc;
+ (void)data;
+ (void)count;
+ return 0;
+#else
+#if 0
+ // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);")
+ drflac_uint8 p = 0x07;
+ for (int i = count-1; i >= 0; --i) {
+ drflac_uint8 bit = (data & (1 << i)) >> i;
+ if (crc & 0x80) {
+ crc = ((crc << 1) | bit) ^ p;
+ } else {
+ crc = ((crc << 1) | bit);
+ }
+ }
+ return crc;
+#else
+ drflac_uint32 wholeBytes = count >> 3;
+ drflac_uint32 leftoverBits = count - (wholeBytes*8);
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+ drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ switch (wholeBytes) {
+ case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
+ case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
+ case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
+ case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
+ case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)];
+ }
+ return crc;
+#endif
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data)
+{
+ return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data];
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount)
+{
+ switch (byteCount)
+ {
+#ifdef DRFLAC_64BIT
+ case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
+ case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
+ case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
+ case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
+#endif
+ case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
+ case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
+ case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
+ case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
+ }
+
+ return crc;
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count)
+{
+ drflac_assert(count <= 64);
+
+#ifdef DR_FLAC_NO_CRC
+ (void)crc;
+ (void)data;
+ (void)count;
+ return 0;
+#else
+#if 0
+ // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);")
+ drflac_uint16 p = 0x8005;
+ for (int i = count-1; i >= 0; --i) {
+ drflac_uint16 bit = (data & (1ULL << i)) >> i;
+ if (r & 0x8000) {
+ r = ((r << 1) | bit) ^ p;
+ } else {
+ r = ((r << 1) | bit);
+ }
+ }
+
+ return crc;
+#else
+ drflac_uint32 wholeBytes = count >> 3;
+ drflac_uint32 leftoverBits = count - (wholeBytes*8);
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+ drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ switch (wholeBytes) {
+ default:
+ case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
+ case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
+ case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
+ case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
+ case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
+ }
+ return crc;
+#endif
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count)
+{
+ drflac_assert(count <= 64);
+
+#ifdef DR_FLAC_NO_CRC
+ (void)crc;
+ (void)data;
+ (void)count;
+ return 0;
+#else
+ drflac_uint32 wholeBytes = count >> 3;
+ drflac_uint32 leftoverBits = count - (wholeBytes*8);
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+ drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ switch (wholeBytes) {
+ default:
+ case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0xFF00000000000000 << leftoverBits)) >> (56 + leftoverBits)));
+ case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00FF000000000000 << leftoverBits)) >> (48 + leftoverBits)));
+ case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000FF0000000000 << leftoverBits)) >> (40 + leftoverBits)));
+ case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000FF00000000 << leftoverBits)) >> (32 + leftoverBits)));
+ case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000FF000000 << leftoverBits)) >> (24 + leftoverBits)));
+ case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000000000FF0000 << leftoverBits)) >> (16 + leftoverBits)));
+ case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000000000FF00 << leftoverBits)) >> ( 8 + leftoverBits)));
+ case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000000000FF << leftoverBits)) >> ( 0 + leftoverBits)));
+ case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
+ }
+ return crc;
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count)
+{
+#ifdef DRFLAC_64BIT
+ return drflac_crc16__64bit(crc, data, count);
+#else
+ return drflac_crc16__32bit(crc, data, count);
+#endif
+}
+
+
+#ifdef DRFLAC_64BIT
+#define drflac__be2host__cache_line drflac__be2host_64
+#else
+#define drflac__be2host__cache_line drflac__be2host_32
+#endif
+
+// BIT READING ATTEMPT #2
+//
+// This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting
+// on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache
+// is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an
+// array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data
+// from onRead() is read into.
+#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
+#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
+#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
+#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount)))
+#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
+#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
+#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
+#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)))
+#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
+#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
+#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
+
+
+#ifndef DR_FLAC_NO_CRC
+static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs)
+{
+ bs->crc16 = 0;
+ bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
+}
+
+static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs)
+{
+ bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
+ bs->crc16CacheIgnoredBytes = 0;
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs)
+{
+ // We should never be flushing in a situation where we are not aligned on a byte boundary.
+ drflac_assert((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
+
+ // The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined
+ // by the number of bits that have been consumed.
+ if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
+ drflac__update_crc16(bs);
+ } else {
+ // We only accumulate the consumed bits.
+ bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
+
+ // The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated
+ // so we can handle that later.
+ bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
+ }
+
+ return bs->crc16;
+}
+#endif
+
+static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs)
+{
+ // Fast path. Try loading straight from L2.
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DRFLAC_TRUE;
+ }
+
+ // If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's
+ // any left.
+ if (bs->unalignedByteCount > 0) {
+ return DRFLAC_FALSE; // If we have any unaligned bytes it means there's no more aligned bytes left in the client.
+ }
+
+ size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
+
+ bs->nextL2Line = 0;
+ if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) {
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DRFLAC_TRUE;
+ }
+
+
+ // If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably
+ // means we've just reached the end of the file. We need to move the valid data down to the end of the buffer
+ // and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to
+ // the size of the L1 so we'll need to seek backwards by any misaligned bytes.
+ size_t alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
+
+ // We need to keep track of any unaligned bytes for later use.
+ bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs));
+ if (bs->unalignedByteCount > 0) {
+ bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
+ }
+
+ if (alignedL1LineCount > 0) {
+ size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
+ for (size_t i = alignedL1LineCount; i > 0; --i) {
+ bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
+ }
+
+ bs->nextL2Line = (drflac_uint32)offset;
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DRFLAC_TRUE;
+ } else {
+ // If we get into this branch it means we weren't able to load any L1-aligned data.
+ bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
+ return DRFLAC_FALSE;
+ }
+}
+
+static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
+{
+#ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+#endif
+
+ // Fast path. Try just moving the next value in the L2 cache to the L1 cache.
+ if (drflac__reload_l1_cache_from_l2(bs)) {
+ bs->cache = drflac__be2host__cache_line(bs->cache);
+ bs->consumedBits = 0;
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs->cache;
+#endif
+ return DRFLAC_TRUE;
+ }
+
+ // Slow path.
+
+ // If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last
+ // few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the
+ // data from the unaligned cache.
+ size_t bytesRead = bs->unalignedByteCount;
+ if (bytesRead == 0) {
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- The stream has been exhausted, so marked the bits as consumed.
+ return DRFLAC_FALSE;
+ }
+
+ drflac_assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs));
+ bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
+
+ bs->cache = drflac__be2host__cache_line(bs->unalignedCache);
+ bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property.
+ bs->unalignedByteCount = 0; // <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes.
+
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs->cache >> bs->consumedBits;
+ bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
+#endif
+ return DRFLAC_TRUE;
+}
+
+static void drflac__reset_cache(drflac_bs* bs)
+{
+ bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); // <-- This clears the L2 cache.
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- This clears the L1 cache.
+ bs->cache = 0;
+ bs->unalignedByteCount = 0; // <-- This clears the trailing unaligned bytes.
+ bs->unalignedCache = 0;
+
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = 0;
+ bs->crc16CacheIgnoredBytes = 0;
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResultOut != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 32);
+
+ if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ // If we want to load all 32-bits from a 32-bit cache we need to do it slightly differently because we can't do
+ // a 32-bit shift on a 32-bit integer. This will never be the case on 64-bit caches, so we can have a slightly
+ // more optimal solution for this.
+#ifdef DRFLAC_64BIT
+ *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
+ bs->consumedBits += bitCount;
+ bs->cache <<= bitCount;
+#else
+ if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
+ bs->consumedBits += bitCount;
+ bs->cache <<= bitCount;
+ } else {
+ // Cannot shift by 32-bits, so need to do it differently.
+ *pResultOut = (drflac_uint32)bs->cache;
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ bs->cache = 0;
+ }
+#endif
+
+ return DRFLAC_TRUE;
+ } else {
+ // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them.
+ drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ drflac_uint32 bitCountLo = bitCount - bitCountHi;
+ drflac_uint32 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
+
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
+ bs->consumedBits += bitCountLo;
+ bs->cache <<= bitCountLo;
+ return DRFLAC_TRUE;
+ }
+}
+
+static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 32);
+
+ drflac_uint32 result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint32 signbit = ((result >> (bitCount-1)) & 0x01);
+ result |= (~signbit + 1) << bitCount;
+
+ *pResult = (drflac_int32)result;
+ return DRFLAC_TRUE;
+}
+
+#ifdef DRFLAC_64BIT
+static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut)
+{
+ drflac_assert(bitCount <= 64);
+ drflac_assert(bitCount > 32);
+
+ drflac_uint32 resultHi;
+ if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint32 resultLo;
+ if (!drflac__read_uint32(bs, 32, &resultLo)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo);
+ return DRFLAC_TRUE;
+}
+#endif
+
+// Function below is unused, but leaving it here in case I need to quickly add it again.
+#if 0
+static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut)
+{
+ drflac_assert(bitCount <= 64);
+
+ drflac_uint64 result;
+ if (!drflac__read_uint64(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint64 signbit = ((result >> (bitCount-1)) & 0x01);
+ result |= (~signbit + 1) << bitCount;
+
+ *pResultOut = (drflac_int64)result;
+ return DRFLAC_TRUE;
+}
+#endif
+
+static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 16);
+
+ drflac_uint32 result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResult = (drflac_uint16)result;
+ return DRFLAC_TRUE;
+}
+
+#if 0
+static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 16);
+
+ drflac_int32 result;
+ if (!drflac__read_int32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResult = (drflac_int16)result;
+ return DRFLAC_TRUE;
+}
+#endif
+
+static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 8);
+
+ drflac_uint32 result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResult = (drflac_uint8)result;
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 8);
+
+ drflac_int32 result;
+ if (!drflac__read_int32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResult = (drflac_int8)result;
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
+{
+ if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ bs->consumedBits += (drflac_uint32)bitsToSeek;
+ bs->cache <<= bitsToSeek;
+ return DRFLAC_TRUE;
+ } else {
+ // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here.
+ bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ bs->cache = 0;
+
+ // Simple case. Seek in groups of the same number as bits that fit within a cache line.
+#ifdef DRFLAC_64BIT
+ while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ drflac_uint64 bin;
+ if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
+ return DRFLAC_FALSE;
+ }
+ bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ }
+#else
+ while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ drflac_uint32 bin;
+ if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
+ return DRFLAC_FALSE;
+ }
+ bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ }
+#endif
+
+ // Whole leftover bytes.
+ while (bitsToSeek >= 8) {
+ drflac_uint8 bin;
+ if (!drflac__read_uint8(bs, 8, &bin)) {
+ return DRFLAC_FALSE;
+ }
+ bitsToSeek -= 8;
+ }
+
+ // Leftover bits.
+ if (bitsToSeek > 0) {
+ drflac_uint8 bin;
+ if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) {
+ return DRFLAC_FALSE;
+ }
+ bitsToSeek = 0; // <-- Necessary for the assert below.
+ }
+
+ drflac_assert(bitsToSeek == 0);
+ return DRFLAC_TRUE;
+ }
+}
+
+
+// This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16.
+static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
+{
+ drflac_assert(bs != NULL);
+
+ // The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first
+ // thing to do is align to the next byte.
+ if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
+ return DRFLAC_FALSE;
+ }
+
+ for (;;) {
+#ifndef DR_FLAC_NO_CRC
+ drflac__reset_crc16(bs);
+#endif
+
+ drflac_uint8 hi;
+ if (!drflac__read_uint8(bs, 8, &hi)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (hi == 0xFF) {
+ drflac_uint8 lo;
+ if (!drflac__read_uint8(bs, 6, &lo)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (lo == 0x3E) {
+ return DRFLAC_TRUE;
+ } else {
+ if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ }
+ }
+
+ // Should never get here.
+ //return DRFLAC_FALSE;
+}
+
+
+#if !defined(DR_FLAC_NO_SIMD) && defined(DRFLAC_HAS_LZCNT_INTRINSIC)
+#define DRFLAC_IMPLEMENT_CLZ_LZCNT
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86))
+#define DRFLAC_IMPLEMENT_CLZ_MSVC
+#endif
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
+{
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
+
+ static drflac_uint32 clz_table_4[] = {
+ 0,
+ 4,
+ 3, 3,
+ 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1
+ };
+
+ drflac_uint32 n = clz_table_4[x >> (sizeof(x)*8 - 4)];
+ if (n == 0) {
+#ifdef DRFLAC_64BIT
+ if ((x & 0xFFFFFFFF00000000ULL) == 0) { n = 32; x <<= 32; }
+ if ((x & 0xFFFF000000000000ULL) == 0) { n += 16; x <<= 16; }
+ if ((x & 0xFF00000000000000ULL) == 0) { n += 8; x <<= 8; }
+ if ((x & 0xF000000000000000ULL) == 0) { n += 4; x <<= 4; }
+#else
+ if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
+ if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
+ if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
+#endif
+ n += clz_table_4[x >> (sizeof(x)*8 - 4)];
+ }
+
+ return n - 1;
+}
+
+#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
+static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported()
+{
+ // If the compiler itself does not support the intrinsic then we'll need to return false.
+#ifdef DRFLAC_HAS_LZCNT_INTRINSIC
+ return drflac__gIsLZCNTSupported;
+#else
+ return DRFLAC_FALSE;
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
+{
+#if defined(_MSC_VER) && !defined(__clang__)
+ #ifdef DRFLAC_64BIT
+ return (drflac_uint32)__lzcnt64(x);
+ #else
+ return (drflac_uint32)__lzcnt(x);
+ #endif
+#else
+ #if defined(__GNUC__) || defined(__clang__)
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
+ #ifdef DRFLAC_64BIT
+ return (drflac_uint32)__builtin_clzll((unsigned long long)x);
+ #else
+ return (drflac_uint32)__builtin_clzl((unsigned long)x);
+ #endif
+ #else
+ // Unsupported compiler.
+ #error "This compiler does not support the lzcnt intrinsic."
+ #endif
+#endif
+}
+#endif
+
+#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
+#include // For BitScanReverse().
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
+{
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
+
+ drflac_uint32 n;
+#ifdef DRFLAC_64BIT
+ _BitScanReverse64((unsigned long*)&n, x);
+#else
+ _BitScanReverse((unsigned long*)&n, x);
+#endif
+ return sizeof(x)*8 - n - 1;
+}
+#endif
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x)
+{
+#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
+ if (drflac__is_lzcnt_supported()) {
+ return drflac__clz_lzcnt(x);
+ } else
+#endif
+ {
+#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
+ return drflac__clz_msvc(x);
+#else
+ return drflac__clz_software(x);
+#endif
+ }
+}
+
+
+static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
+{
+ drflac_uint32 zeroCounter = 0;
+ while (bs->cache == 0) {
+ zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache);
+ setBitOffsetPlus1 += 1;
+
+ bs->consumedBits += setBitOffsetPlus1;
+ bs->cache <<= setBitOffsetPlus1;
+
+ *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
+ return DRFLAC_TRUE;
+}
+
+
+
+static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(offsetFromStart > 0);
+
+ // Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which
+ // is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit.
+ // To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder.
+ if (offsetFromStart > 0x7FFFFFFF) {
+ drflac_uint64 bytesRemaining = offsetFromStart;
+ if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ bytesRemaining -= 0x7FFFFFFF;
+
+ while (bytesRemaining > 0x7FFFFFFF) {
+ if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ bytesRemaining -= 0x7FFFFFFF;
+ }
+
+ if (bytesRemaining > 0) {
+ if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ // The cache should be reset to force a reload of fresh data from the client.
+ drflac__reset_cache(bs);
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pNumberOut != NULL);
+
+ drflac_uint8 crc = *pCRCOut;
+
+ unsigned char utf8[7] = {0};
+ if (!drflac__read_uint8(bs, 8, utf8)) {
+ *pNumberOut = 0;
+ return DRFLAC_END_OF_STREAM;
+ }
+ crc = drflac_crc8(crc, utf8[0], 8);
+
+ if ((utf8[0] & 0x80) == 0) {
+ *pNumberOut = utf8[0];
+ *pCRCOut = crc;
+ return DRFLAC_SUCCESS;
+ }
+
+ int byteCount = 1;
+ if ((utf8[0] & 0xE0) == 0xC0) {
+ byteCount = 2;
+ } else if ((utf8[0] & 0xF0) == 0xE0) {
+ byteCount = 3;
+ } else if ((utf8[0] & 0xF8) == 0xF0) {
+ byteCount = 4;
+ } else if ((utf8[0] & 0xFC) == 0xF8) {
+ byteCount = 5;
+ } else if ((utf8[0] & 0xFE) == 0xFC) {
+ byteCount = 6;
+ } else if ((utf8[0] & 0xFF) == 0xFE) {
+ byteCount = 7;
+ } else {
+ *pNumberOut = 0;
+ return DRFLAC_CRC_MISMATCH; // Bad UTF-8 encoding.
+ }
+
+ // Read extra bytes.
+ drflac_assert(byteCount > 1);
+
+ drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
+ for (int i = 1; i < byteCount; ++i) {
+ if (!drflac__read_uint8(bs, 8, utf8 + i)) {
+ *pNumberOut = 0;
+ return DRFLAC_END_OF_STREAM;
+ }
+ crc = drflac_crc8(crc, utf8[i], 8);
+
+ result = (result << 6) | (utf8[i] & 0x3F);
+ }
+
+ *pNumberOut = result;
+ *pCRCOut = crc;
+ return DRFLAC_SUCCESS;
+}
+
+
+
+
+// The next two functions are responsible for calculating the prediction.
+//
+// When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's
+// safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16.
+static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ // 32-bit version.
+
+ // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers.
+ drflac_int32 prediction = 0;
+
+ switch (order)
+ {
+ case 32: prediction += coefficients[31] * pDecodedSamples[-32];
+ case 31: prediction += coefficients[30] * pDecodedSamples[-31];
+ case 30: prediction += coefficients[29] * pDecodedSamples[-30];
+ case 29: prediction += coefficients[28] * pDecodedSamples[-29];
+ case 28: prediction += coefficients[27] * pDecodedSamples[-28];
+ case 27: prediction += coefficients[26] * pDecodedSamples[-27];
+ case 26: prediction += coefficients[25] * pDecodedSamples[-26];
+ case 25: prediction += coefficients[24] * pDecodedSamples[-25];
+ case 24: prediction += coefficients[23] * pDecodedSamples[-24];
+ case 23: prediction += coefficients[22] * pDecodedSamples[-23];
+ case 22: prediction += coefficients[21] * pDecodedSamples[-22];
+ case 21: prediction += coefficients[20] * pDecodedSamples[-21];
+ case 20: prediction += coefficients[19] * pDecodedSamples[-20];
+ case 19: prediction += coefficients[18] * pDecodedSamples[-19];
+ case 18: prediction += coefficients[17] * pDecodedSamples[-18];
+ case 17: prediction += coefficients[16] * pDecodedSamples[-17];
+ case 16: prediction += coefficients[15] * pDecodedSamples[-16];
+ case 15: prediction += coefficients[14] * pDecodedSamples[-15];
+ case 14: prediction += coefficients[13] * pDecodedSamples[-14];
+ case 13: prediction += coefficients[12] * pDecodedSamples[-13];
+ case 12: prediction += coefficients[11] * pDecodedSamples[-12];
+ case 11: prediction += coefficients[10] * pDecodedSamples[-11];
+ case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
+ case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
+ case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
+ case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
+ case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
+ case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
+ case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
+ case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
+ case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
+ case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
+ }
+
+ return (drflac_int32)(prediction >> shift);
+}
+
+static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ // 64-bit version.
+
+ // This method is faster on the 32-bit build when compiling with VC++. See note below.
+#ifndef DRFLAC_64BIT
+ drflac_int64 prediction;
+ if (order == 8)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ }
+ else if (order == 7)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ }
+ else if (order == 3)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ }
+ else if (order == 6)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ }
+ else if (order == 5)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ }
+ else if (order == 4)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ }
+ else if (order == 12)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
+ prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
+ prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
+ prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
+ }
+ else if (order == 2)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ }
+ else if (order == 1)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ }
+ else if (order == 10)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
+ prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
+ }
+ else if (order == 9)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
+ }
+ else if (order == 11)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
+ prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
+ prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
+ }
+ else
+ {
+ prediction = 0;
+ for (int j = 0; j < (int)order; ++j) {
+ prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1];
+ }
+ }
+#endif
+
+ // VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some
+ // reason. The ugly version above is faster so we'll just switch between the two depending on the target platform.
+#ifdef DRFLAC_64BIT
+ drflac_int64 prediction = 0;
+
+ switch (order)
+ {
+ case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
+ case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31];
+ case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30];
+ case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29];
+ case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28];
+ case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27];
+ case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26];
+ case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25];
+ case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24];
+ case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23];
+ case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22];
+ case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21];
+ case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20];
+ case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19];
+ case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18];
+ case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17];
+ case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16];
+ case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15];
+ case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14];
+ case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13];
+ case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
+ case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
+ case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10];
+ case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9];
+ case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8];
+ case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7];
+ case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6];
+ case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5];
+ case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4];
+ case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3];
+ case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2];
+ case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
+ }
+#endif
+
+ return (drflac_int32)(prediction >> shift);
+}
+
+static DRFLAC_INLINE void drflac__calculate_prediction_64_x4(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, const drflac_uint32 riceParamParts[4], drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ drflac_int64 prediction0 = 0;
+ drflac_int64 prediction1 = 0;
+ drflac_int64 prediction2 = 0;
+ drflac_int64 prediction3 = 0;
+
+ switch (order)
+ {
+ case 32:
+ prediction0 += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
+ prediction1 += coefficients[31] * (drflac_int64)pDecodedSamples[-31];
+ prediction2 += coefficients[31] * (drflac_int64)pDecodedSamples[-30];
+ prediction3 += coefficients[31] * (drflac_int64)pDecodedSamples[-29];
+ case 31:
+ prediction0 += coefficients[30] * (drflac_int64)pDecodedSamples[-31];
+ prediction1 += coefficients[30] * (drflac_int64)pDecodedSamples[-30];
+ prediction2 += coefficients[30] * (drflac_int64)pDecodedSamples[-29];
+ prediction3 += coefficients[30] * (drflac_int64)pDecodedSamples[-28];
+ case 30:
+ prediction0 += coefficients[29] * (drflac_int64)pDecodedSamples[-30];
+ prediction1 += coefficients[29] * (drflac_int64)pDecodedSamples[-29];
+ prediction2 += coefficients[29] * (drflac_int64)pDecodedSamples[-28];
+ prediction3 += coefficients[29] * (drflac_int64)pDecodedSamples[-27];
+ case 29:
+ prediction0 += coefficients[28] * (drflac_int64)pDecodedSamples[-29];
+ prediction1 += coefficients[28] * (drflac_int64)pDecodedSamples[-28];
+ prediction2 += coefficients[28] * (drflac_int64)pDecodedSamples[-27];
+ prediction3 += coefficients[28] * (drflac_int64)pDecodedSamples[-26];
+ case 28:
+ prediction0 += coefficients[27] * (drflac_int64)pDecodedSamples[-28];
+ prediction1 += coefficients[27] * (drflac_int64)pDecodedSamples[-27];
+ prediction2 += coefficients[27] * (drflac_int64)pDecodedSamples[-26];
+ prediction3 += coefficients[27] * (drflac_int64)pDecodedSamples[-25];
+ case 27:
+ prediction0 += coefficients[26] * (drflac_int64)pDecodedSamples[-27];
+ prediction1 += coefficients[26] * (drflac_int64)pDecodedSamples[-26];
+ prediction2 += coefficients[26] * (drflac_int64)pDecodedSamples[-25];
+ prediction3 += coefficients[26] * (drflac_int64)pDecodedSamples[-24];
+ case 26:
+ prediction0 += coefficients[25] * (drflac_int64)pDecodedSamples[-26];
+ prediction1 += coefficients[25] * (drflac_int64)pDecodedSamples[-25];
+ prediction2 += coefficients[25] * (drflac_int64)pDecodedSamples[-24];
+ prediction3 += coefficients[25] * (drflac_int64)pDecodedSamples[-23];
+ case 25:
+ prediction0 += coefficients[24] * (drflac_int64)pDecodedSamples[-25];
+ prediction1 += coefficients[24] * (drflac_int64)pDecodedSamples[-24];
+ prediction2 += coefficients[24] * (drflac_int64)pDecodedSamples[-23];
+ prediction3 += coefficients[24] * (drflac_int64)pDecodedSamples[-22];
+ case 24:
+ prediction0 += coefficients[23] * (drflac_int64)pDecodedSamples[-24];
+ prediction1 += coefficients[23] * (drflac_int64)pDecodedSamples[-23];
+ prediction2 += coefficients[23] * (drflac_int64)pDecodedSamples[-22];
+ prediction3 += coefficients[23] * (drflac_int64)pDecodedSamples[-21];
+ case 23:
+ prediction0 += coefficients[22] * (drflac_int64)pDecodedSamples[-23];
+ prediction1 += coefficients[22] * (drflac_int64)pDecodedSamples[-22];
+ prediction2 += coefficients[22] * (drflac_int64)pDecodedSamples[-21];
+ prediction3 += coefficients[22] * (drflac_int64)pDecodedSamples[-20];
+ case 22:
+ prediction0 += coefficients[21] * (drflac_int64)pDecodedSamples[-22];
+ prediction1 += coefficients[21] * (drflac_int64)pDecodedSamples[-21];
+ prediction2 += coefficients[21] * (drflac_int64)pDecodedSamples[-20];
+ prediction3 += coefficients[21] * (drflac_int64)pDecodedSamples[-19];
+ case 21:
+ prediction0 += coefficients[20] * (drflac_int64)pDecodedSamples[-21];
+ prediction1 += coefficients[20] * (drflac_int64)pDecodedSamples[-20];
+ prediction2 += coefficients[20] * (drflac_int64)pDecodedSamples[-19];
+ prediction3 += coefficients[20] * (drflac_int64)pDecodedSamples[-18];
+ case 20:
+ prediction0 += coefficients[19] * (drflac_int64)pDecodedSamples[-20];
+ prediction1 += coefficients[19] * (drflac_int64)pDecodedSamples[-19];
+ prediction2 += coefficients[19] * (drflac_int64)pDecodedSamples[-18];
+ prediction3 += coefficients[19] * (drflac_int64)pDecodedSamples[-17];
+ case 19:
+ prediction0 += coefficients[18] * (drflac_int64)pDecodedSamples[-19];
+ prediction1 += coefficients[18] * (drflac_int64)pDecodedSamples[-18];
+ prediction2 += coefficients[18] * (drflac_int64)pDecodedSamples[-17];
+ prediction3 += coefficients[18] * (drflac_int64)pDecodedSamples[-16];
+ case 18:
+ prediction0 += coefficients[17] * (drflac_int64)pDecodedSamples[-18];
+ prediction1 += coefficients[17] * (drflac_int64)pDecodedSamples[-17];
+ prediction2 += coefficients[17] * (drflac_int64)pDecodedSamples[-16];
+ prediction3 += coefficients[17] * (drflac_int64)pDecodedSamples[-15];
+ case 17:
+ prediction0 += coefficients[16] * (drflac_int64)pDecodedSamples[-17];
+ prediction1 += coefficients[16] * (drflac_int64)pDecodedSamples[-16];
+ prediction2 += coefficients[16] * (drflac_int64)pDecodedSamples[-15];
+ prediction3 += coefficients[16] * (drflac_int64)pDecodedSamples[-14];
+
+ case 16:
+ prediction0 += coefficients[15] * (drflac_int64)pDecodedSamples[-16];
+ prediction1 += coefficients[15] * (drflac_int64)pDecodedSamples[-15];
+ prediction2 += coefficients[15] * (drflac_int64)pDecodedSamples[-14];
+ prediction3 += coefficients[15] * (drflac_int64)pDecodedSamples[-13];
+ case 15:
+ prediction0 += coefficients[14] * (drflac_int64)pDecodedSamples[-15];
+ prediction1 += coefficients[14] * (drflac_int64)pDecodedSamples[-14];
+ prediction2 += coefficients[14] * (drflac_int64)pDecodedSamples[-13];
+ prediction3 += coefficients[14] * (drflac_int64)pDecodedSamples[-12];
+ case 14:
+ prediction0 += coefficients[13] * (drflac_int64)pDecodedSamples[-14];
+ prediction1 += coefficients[13] * (drflac_int64)pDecodedSamples[-13];
+ prediction2 += coefficients[13] * (drflac_int64)pDecodedSamples[-12];
+ prediction3 += coefficients[13] * (drflac_int64)pDecodedSamples[-11];
+ case 13:
+ prediction0 += coefficients[12] * (drflac_int64)pDecodedSamples[-13];
+ prediction1 += coefficients[12] * (drflac_int64)pDecodedSamples[-12];
+ prediction2 += coefficients[12] * (drflac_int64)pDecodedSamples[-11];
+ prediction3 += coefficients[12] * (drflac_int64)pDecodedSamples[-10];
+ case 12:
+ prediction0 += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
+ prediction1 += coefficients[11] * (drflac_int64)pDecodedSamples[-11];
+ prediction2 += coefficients[11] * (drflac_int64)pDecodedSamples[-10];
+ prediction3 += coefficients[11] * (drflac_int64)pDecodedSamples[- 9];
+ case 11:
+ prediction0 += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
+ prediction1 += coefficients[10] * (drflac_int64)pDecodedSamples[-10];
+ prediction2 += coefficients[10] * (drflac_int64)pDecodedSamples[- 9];
+ prediction3 += coefficients[10] * (drflac_int64)pDecodedSamples[- 8];
+ case 10:
+ prediction0 += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
+ prediction1 += coefficients[9] * (drflac_int64)pDecodedSamples[- 9];
+ prediction2 += coefficients[9] * (drflac_int64)pDecodedSamples[- 8];
+ prediction3 += coefficients[9] * (drflac_int64)pDecodedSamples[- 7];
+ case 9:
+ prediction0 += coefficients[8] * (drflac_int64)pDecodedSamples[- 9];
+ prediction1 += coefficients[8] * (drflac_int64)pDecodedSamples[- 8];
+ prediction2 += coefficients[8] * (drflac_int64)pDecodedSamples[- 7];
+ prediction3 += coefficients[8] * (drflac_int64)pDecodedSamples[- 6];
+ case 8:
+ prediction0 += coefficients[7] * (drflac_int64)pDecodedSamples[- 8];
+ prediction1 += coefficients[7] * (drflac_int64)pDecodedSamples[- 7];
+ prediction2 += coefficients[7] * (drflac_int64)pDecodedSamples[- 6];
+ prediction3 += coefficients[7] * (drflac_int64)pDecodedSamples[- 5];
+ case 7:
+ prediction0 += coefficients[6] * (drflac_int64)pDecodedSamples[- 7];
+ prediction1 += coefficients[6] * (drflac_int64)pDecodedSamples[- 6];
+ prediction2 += coefficients[6] * (drflac_int64)pDecodedSamples[- 5];
+ prediction3 += coefficients[6] * (drflac_int64)pDecodedSamples[- 4];
+ case 6:
+ prediction0 += coefficients[5] * (drflac_int64)pDecodedSamples[- 6];
+ prediction1 += coefficients[5] * (drflac_int64)pDecodedSamples[- 5];
+ prediction2 += coefficients[5] * (drflac_int64)pDecodedSamples[- 4];
+ prediction3 += coefficients[5] * (drflac_int64)pDecodedSamples[- 3];
+ case 5:
+ prediction0 += coefficients[4] * (drflac_int64)pDecodedSamples[- 5];
+ prediction1 += coefficients[4] * (drflac_int64)pDecodedSamples[- 4];
+ prediction2 += coefficients[4] * (drflac_int64)pDecodedSamples[- 3];
+ prediction3 += coefficients[4] * (drflac_int64)pDecodedSamples[- 2];
+ case 4:
+ prediction0 += coefficients[3] * (drflac_int64)pDecodedSamples[- 4];
+ prediction1 += coefficients[3] * (drflac_int64)pDecodedSamples[- 3];
+ prediction2 += coefficients[3] * (drflac_int64)pDecodedSamples[- 2];
+ prediction3 += coefficients[3] * (drflac_int64)pDecodedSamples[- 1];
+ order = 3;
+ }
+
+ switch (order)
+ {
+ case 3: prediction0 += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3];
+ case 2: prediction0 += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2];
+ case 1: prediction0 += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
+ }
+ pDecodedSamples[0] = riceParamParts[0] + (drflac_int32)(prediction0 >> shift);
+
+ switch (order)
+ {
+ case 3: prediction1 += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 2];
+ case 2: prediction1 += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 1];
+ case 1: prediction1 += coefficients[ 0] * (drflac_int64)pDecodedSamples[ 0];
+ }
+ pDecodedSamples[1] = riceParamParts[1] + (drflac_int32)(prediction1 >> shift);
+
+ switch (order)
+ {
+ case 3: prediction2 += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 1];
+ case 2: prediction2 += coefficients[ 1] * (drflac_int64)pDecodedSamples[ 0];
+ case 1: prediction2 += coefficients[ 0] * (drflac_int64)pDecodedSamples[ 1];
+ }
+ pDecodedSamples[2] = riceParamParts[2] + (drflac_int32)(prediction2 >> shift);
+
+ switch (order)
+ {
+ case 3: prediction3 += coefficients[ 2] * (drflac_int64)pDecodedSamples[ 0];
+ case 2: prediction3 += coefficients[ 1] * (drflac_int64)pDecodedSamples[ 1];
+ case 1: prediction3 += coefficients[ 0] * (drflac_int64)pDecodedSamples[ 2];
+ }
+ pDecodedSamples[3] = riceParamParts[3] + (drflac_int32)(prediction3 >> shift);
+}
+
+#if defined(DRFLAC_SUPPORT_SSE41)
+static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64__sse41(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ __m128i prediction = _mm_setzero_si128();
+
+ switch (order)
+ {
+ case 32:
+ case 31: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[31], 0, coefficients[30]), _mm_set_epi32(0, pDecodedSamples[-32], 0, pDecodedSamples[-31])));
+ case 30:
+ case 29: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[29], 0, coefficients[28]), _mm_set_epi32(0, pDecodedSamples[-30], 0, pDecodedSamples[-29])));
+ case 28:
+ case 27: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[27], 0, coefficients[26]), _mm_set_epi32(0, pDecodedSamples[-28], 0, pDecodedSamples[-27])));
+ case 26:
+ case 25: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[25], 0, coefficients[24]), _mm_set_epi32(0, pDecodedSamples[-26], 0, pDecodedSamples[-25])));
+ case 24:
+ case 23: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[23], 0, coefficients[22]), _mm_set_epi32(0, pDecodedSamples[-24], 0, pDecodedSamples[-23])));
+ case 22:
+ case 21: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[21], 0, coefficients[20]), _mm_set_epi32(0, pDecodedSamples[-22], 0, pDecodedSamples[-21])));
+ case 20:
+ case 19: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[19], 0, coefficients[18]), _mm_set_epi32(0, pDecodedSamples[-20], 0, pDecodedSamples[-19])));
+ case 18:
+ case 17: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[17], 0, coefficients[16]), _mm_set_epi32(0, pDecodedSamples[-18], 0, pDecodedSamples[-17])));
+ case 16:
+ case 15: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[15], 0, coefficients[14]), _mm_set_epi32(0, pDecodedSamples[-16], 0, pDecodedSamples[-15])));
+ case 14:
+ case 13: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[13], 0, coefficients[12]), _mm_set_epi32(0, pDecodedSamples[-14], 0, pDecodedSamples[-13])));
+ case 12:
+ case 11: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[11], 0, coefficients[10]), _mm_set_epi32(0, pDecodedSamples[-12], 0, pDecodedSamples[-11])));
+ case 10:
+ case 9: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 9], 0, coefficients[ 8]), _mm_set_epi32(0, pDecodedSamples[-10], 0, pDecodedSamples[- 9])));
+ case 8:
+ case 7: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 7], 0, coefficients[ 6]), _mm_set_epi32(0, pDecodedSamples[- 8], 0, pDecodedSamples[- 7])));
+ case 6:
+ case 5: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 5], 0, coefficients[ 4]), _mm_set_epi32(0, pDecodedSamples[- 6], 0, pDecodedSamples[- 5])));
+ case 4:
+ case 3: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 3], 0, coefficients[ 2]), _mm_set_epi32(0, pDecodedSamples[- 4], 0, pDecodedSamples[- 3])));
+ case 2:
+ case 1: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 1], 0, coefficients[ 0]), _mm_set_epi32(0, pDecodedSamples[- 2], 0, pDecodedSamples[- 1])));
+ }
+
+ return (drflac_int32)((
+ ((drflac_uint64*)&prediction)[0] +
+ ((drflac_uint64*)&prediction)[1]) >> shift);
+}
+
+static DRFLAC_INLINE void drflac__calculate_prediction_64_x2__sse41(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, const drflac_uint32 riceParamParts[4], drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ __m128i prediction = _mm_setzero_si128();
+ drflac_int64 predictions[2] = {0, 0};
+
+ switch (order)
+ {
+ case 32: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[31], 0, coefficients[31]), _mm_set_epi32(0, pDecodedSamples[-31], 0, pDecodedSamples[-32])));
+ case 31: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[30], 0, coefficients[30]), _mm_set_epi32(0, pDecodedSamples[-30], 0, pDecodedSamples[-31])));
+ case 30: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[29], 0, coefficients[29]), _mm_set_epi32(0, pDecodedSamples[-29], 0, pDecodedSamples[-30])));
+ case 29: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[28], 0, coefficients[28]), _mm_set_epi32(0, pDecodedSamples[-28], 0, pDecodedSamples[-29])));
+ case 28: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[27], 0, coefficients[27]), _mm_set_epi32(0, pDecodedSamples[-27], 0, pDecodedSamples[-28])));
+ case 27: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[26], 0, coefficients[26]), _mm_set_epi32(0, pDecodedSamples[-26], 0, pDecodedSamples[-27])));
+ case 26: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[25], 0, coefficients[25]), _mm_set_epi32(0, pDecodedSamples[-25], 0, pDecodedSamples[-26])));
+ case 25: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[24], 0, coefficients[24]), _mm_set_epi32(0, pDecodedSamples[-24], 0, pDecodedSamples[-25])));
+ case 24: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[23], 0, coefficients[23]), _mm_set_epi32(0, pDecodedSamples[-23], 0, pDecodedSamples[-24])));
+ case 23: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[22], 0, coefficients[22]), _mm_set_epi32(0, pDecodedSamples[-22], 0, pDecodedSamples[-23])));
+ case 22: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[21], 0, coefficients[21]), _mm_set_epi32(0, pDecodedSamples[-21], 0, pDecodedSamples[-22])));
+ case 21: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[20], 0, coefficients[20]), _mm_set_epi32(0, pDecodedSamples[-20], 0, pDecodedSamples[-21])));
+ case 20: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[19], 0, coefficients[19]), _mm_set_epi32(0, pDecodedSamples[-19], 0, pDecodedSamples[-20])));
+ case 19: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[18], 0, coefficients[18]), _mm_set_epi32(0, pDecodedSamples[-18], 0, pDecodedSamples[-19])));
+ case 18: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[17], 0, coefficients[17]), _mm_set_epi32(0, pDecodedSamples[-17], 0, pDecodedSamples[-18])));
+ case 17: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[16], 0, coefficients[16]), _mm_set_epi32(0, pDecodedSamples[-16], 0, pDecodedSamples[-17])));
+ case 16: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[15], 0, coefficients[15]), _mm_set_epi32(0, pDecodedSamples[-15], 0, pDecodedSamples[-16])));
+ case 15: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[14], 0, coefficients[14]), _mm_set_epi32(0, pDecodedSamples[-14], 0, pDecodedSamples[-15])));
+ case 14: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[13], 0, coefficients[13]), _mm_set_epi32(0, pDecodedSamples[-13], 0, pDecodedSamples[-14])));
+ case 13: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[12], 0, coefficients[12]), _mm_set_epi32(0, pDecodedSamples[-12], 0, pDecodedSamples[-13])));
+ case 12: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[11], 0, coefficients[11]), _mm_set_epi32(0, pDecodedSamples[-11], 0, pDecodedSamples[-12])));
+ case 11: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[10], 0, coefficients[10]), _mm_set_epi32(0, pDecodedSamples[-10], 0, pDecodedSamples[-11])));
+ case 10: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 9], 0, coefficients[ 9]), _mm_set_epi32(0, pDecodedSamples[- 9], 0, pDecodedSamples[-10])));
+ case 9: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 8], 0, coefficients[ 8]), _mm_set_epi32(0, pDecodedSamples[- 8], 0, pDecodedSamples[- 9])));
+ case 8: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 7], 0, coefficients[ 7]), _mm_set_epi32(0, pDecodedSamples[- 7], 0, pDecodedSamples[- 8])));
+ case 7: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 6], 0, coefficients[ 6]), _mm_set_epi32(0, pDecodedSamples[- 6], 0, pDecodedSamples[- 7])));
+ case 6: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 5], 0, coefficients[ 5]), _mm_set_epi32(0, pDecodedSamples[- 5], 0, pDecodedSamples[- 6])));
+ case 5: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 4], 0, coefficients[ 4]), _mm_set_epi32(0, pDecodedSamples[- 4], 0, pDecodedSamples[- 5])));
+ case 4: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 3], 0, coefficients[ 3]), _mm_set_epi32(0, pDecodedSamples[- 3], 0, pDecodedSamples[- 4])));
+ case 3: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 2], 0, coefficients[ 2]), _mm_set_epi32(0, pDecodedSamples[- 2], 0, pDecodedSamples[- 3])));
+ case 2: prediction = _mm_add_epi64(prediction, _mm_mul_epi32(_mm_set_epi32(0, coefficients[ 1], 0, coefficients[ 1]), _mm_set_epi32(0, pDecodedSamples[- 1], 0, pDecodedSamples[- 2])));
+ order = 1;
+ }
+
+ _mm_storeu_si128((__m128i*)predictions, prediction);
+
+ switch (order)
+ {
+ case 1: predictions[0] += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
+ }
+ pDecodedSamples[0] = riceParamParts[0] + (drflac_int32)(predictions[0] >> shift);
+
+ switch (order)
+ {
+ case 1: predictions[1] += coefficients[ 0] * (drflac_int64)pDecodedSamples[ 0];
+ }
+ pDecodedSamples[1] = riceParamParts[1] + (drflac_int32)(predictions[1] >> shift);
+}
+
+
+static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a)
+{
+ return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_slide1_epi32(__m128i a, __m128i b)
+{
+ // a3a2a1a0/b3b2b1b0 -> a2a1a0b3
+
+ // Result = a2a1a0b3
+ __m128i b3a3b2a2 = _mm_unpackhi_epi32(a, b);
+ __m128i a2b3a2b3 = _mm_shuffle_epi32(b3a3b2a2, _MM_SHUFFLE(0, 3, 0, 3));
+ __m128i a1a2a0b3 = _mm_unpacklo_epi32(a2b3a2b3, a);
+ __m128i a2a1a0b3 = _mm_shuffle_epi32(a1a2a0b3, _MM_SHUFFLE(2, 3, 1, 0));
+ return a2a1a0b3;
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_slide2_epi32(__m128i a, __m128i b)
+{
+ // Result = a1a0b3b2
+ __m128i b1b0b3b2 = _mm_shuffle_epi32(b, _MM_SHUFFLE(1, 0, 3, 2));
+ __m128i a1b3a0b2 = _mm_unpacklo_epi32(b1b0b3b2, a);
+ __m128i a1a0b3b2 = _mm_shuffle_epi32(a1b3a0b2, _MM_SHUFFLE(3, 1, 2, 0));
+ return a1a0b3b2;
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_slide3_epi32(__m128i a, __m128i b)
+{
+ // Result = a0b3b2b1
+ __m128i b1a1b0a0 = _mm_unpacklo_epi32(a, b);
+ __m128i a0b1a0b1 = _mm_shuffle_epi32(b1a1b0a0, _MM_SHUFFLE(0, 3, 0, 3));
+ __m128i b3a0b2b1 = _mm_unpackhi_epi32(a0b1a0b1, b);
+ __m128i a0b3b2b1 = _mm_shuffle_epi32(b3a0b2b1, _MM_SHUFFLE(2, 3, 1, 0));
+ return a0b3b2b1;
+}
+
+static DRFLAC_INLINE void drflac__calculate_prediction_32_x4__sse41(drflac_uint32 order, drflac_int32 shift, const __m128i* coefficients128, const __m128i riceParamParts128, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ // I don't think this is as efficient as it could be. More work needs to be done on this.
+ if (order > 0) {
+ __m128i s_09_10_11_12 = _mm_loadu_si128((const __m128i*)(pDecodedSamples - 12));
+ __m128i s_05_06_07_08 = _mm_loadu_si128((const __m128i*)(pDecodedSamples - 8));
+ __m128i s_01_02_03_04 = _mm_loadu_si128((const __m128i*)(pDecodedSamples - 4));
+
+ __m128i prediction = _mm_setzero_si128();
+
+ // The idea with this switch is to do do a single jump based on the value of "order". In my test library, "order" is never larger than 12, so
+ // I have decided to do a less optimal solution in the order > 12 case.
+ switch (order)
+ {
+ case 32: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[31], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 32))));
+ case 31: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[30], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 31))));
+ case 30: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[29], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 30))));
+ case 29: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[28], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 29))));
+ case 28: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[27], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 28))));
+ case 27: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[26], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 27))));
+ case 26: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[25], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 26))));
+ case 25: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[24], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 25))));
+ case 24: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[23], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 24))));
+ case 23: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[22], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 23))));
+ case 22: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[21], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 22))));
+ case 21: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[20], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 21))));
+ case 20: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[19], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 20))));
+ case 19: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[18], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 19))));
+ case 18: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[17], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 18))));
+ case 17: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[16], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 17))));
+ case 16: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[15], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 16))));
+ case 15: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[14], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 15))));
+ case 14: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[13], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 14))));
+ case 13: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[12], _mm_loadu_si128((const __m128i*)(pDecodedSamples - 13))));
+
+ case 12: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[11], s_09_10_11_12));
+ case 11: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[10], drflac__mm_slide3_epi32(s_05_06_07_08, s_09_10_11_12)));
+ case 10: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 9], drflac__mm_slide2_epi32(s_05_06_07_08, s_09_10_11_12)));
+ case 9: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 8], drflac__mm_slide1_epi32(s_05_06_07_08, s_09_10_11_12)));
+ case 8: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 7], s_05_06_07_08));
+ case 7: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 6], drflac__mm_slide3_epi32(s_01_02_03_04, s_05_06_07_08)));
+ case 6: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 5], drflac__mm_slide2_epi32(s_01_02_03_04, s_05_06_07_08)));
+ case 5: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 4], drflac__mm_slide1_epi32(s_01_02_03_04, s_05_06_07_08)));
+ case 4: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 3], s_01_02_03_04)); order = 3; // <-- Don't forget to set order to 3 here!
+ case 3: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 2], drflac__mm_slide3_epi32(_mm_setzero_si128(), s_01_02_03_04)));
+ case 2: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 1], drflac__mm_slide2_epi32(_mm_setzero_si128(), s_01_02_03_04)));
+ case 1: prediction = _mm_add_epi32(prediction, _mm_mullo_epi32(coefficients128[ 0], drflac__mm_slide1_epi32(_mm_setzero_si128(), s_01_02_03_04)));
+ }
+
+ drflac_int32 predictions[4];
+ _mm_storeu_si128((__m128i*)predictions, prediction);
+
+ drflac_uint32 riceParamParts[4];
+ _mm_storeu_si128((__m128i*)riceParamParts, riceParamParts128);
+
+ predictions[0] = riceParamParts[0] + (predictions[0] >> shift);
+
+ switch (order)
+ {
+ case 3: predictions[3] += ((const drflac_int32*)&coefficients128[ 2])[0] * predictions[ 0];
+ case 2: predictions[2] += ((const drflac_int32*)&coefficients128[ 1])[0] * predictions[ 0];
+ case 1: predictions[1] += ((const drflac_int32*)&coefficients128[ 0])[0] * predictions[ 0];
+ }
+ predictions[1] = riceParamParts[1] + (predictions[1] >> shift);
+
+ switch (order)
+ {
+ case 3:
+ case 2: predictions[3] += ((const drflac_int32*)&coefficients128[ 1])[0] * predictions[ 1];
+ case 1: predictions[2] += ((const drflac_int32*)&coefficients128[ 0])[0] * predictions[ 1];
+ }
+ predictions[2] = riceParamParts[2] + (predictions[2] >> shift);
+
+ switch (order)
+ {
+ case 3:
+ case 2:
+ case 1: predictions[3] += ((const drflac_int32*)&coefficients128[ 0])[0] * predictions[ 2];
+ }
+ predictions[3] = riceParamParts[3] + (predictions[3] >> shift);
+
+ pDecodedSamples[0] = predictions[0];
+ pDecodedSamples[1] = predictions[1];
+ pDecodedSamples[2] = predictions[2];
+ pDecodedSamples[3] = predictions[3];
+ } else {
+ _mm_storeu_si128((__m128i*)pDecodedSamples, riceParamParts128);
+ }
+}
+#endif
+
+#if 0
+// Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the
+// sake of readability and should only be used as a reference.
+static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(pSamplesOut != NULL);
+
+ for (drflac_uint32 i = 0; i < count; ++i) {
+ drflac_uint32 zeroCounter = 0;
+ for (;;) {
+ drflac_uint8 bit;
+ if (!drflac__read_uint8(bs, 1, &bit)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (bit == 0) {
+ zeroCounter += 1;
+ } else {
+ break;
+ }
+ }
+
+ drflac_uint32 decodedRice;
+ if (riceParam > 0) {
+ if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ decodedRice = 0;
+ }
+
+ decodedRice |= (zeroCounter << riceParam);
+ if ((decodedRice & 0x01)) {
+ decodedRice = ~(decodedRice >> 1);
+ } else {
+ decodedRice = (decodedRice >> 1);
+ }
+
+
+ if (bitsPerSample > 16) {
+ pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
+ } else {
+ pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+#endif
+
+#if 0
+static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_uint32 zeroCounter = 0;
+ for (;;) {
+ drflac_uint8 bit;
+ if (!drflac__read_uint8(bs, 1, &bit)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (bit == 0) {
+ zeroCounter += 1;
+ } else {
+ break;
+ }
+ }
+
+ drflac_uint32 decodedRice;
+ if (riceParam > 0) {
+ if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ decodedRice = 0;
+ }
+
+ *pZeroCounterOut = zeroCounter;
+ *pRiceParamPartOut = decodedRice;
+ return DRFLAC_TRUE;
+}
+#endif
+
+#if 0
+static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_assert(riceParam > 0); // <-- riceParam should never be 0. drflac__read_rice_parts__param_equals_zero() should be used instead for this case.
+
+ drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
+
+ drflac_uint32 zeroCounter = 0;
+ while (bs->cache == 0) {
+ zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache);
+ zeroCounter += setBitOffsetPlus1;
+ setBitOffsetPlus1 += 1;
+
+
+ drflac_uint32 riceParamPart;
+ drflac_uint32 riceLength = setBitOffsetPlus1 + riceParam;
+ if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
+
+ bs->consumedBits += riceLength;
+ bs->cache <<= riceLength;
+ } else {
+ bs->consumedBits += riceLength;
+ bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); // <-- Equivalent to "if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { bs->cache <<= setBitOffsetPlus1; }"
+
+ // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them.
+ drflac_uint32 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ drflac_cache_t resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); // <-- Use DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE() if ever this function allows riceParam=0.
+
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+#ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+#endif
+ bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs->consumedBits = 0;
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs->cache;
+#endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
+
+ bs->consumedBits += bitCountLo;
+ bs->cache <<= bitCountLo;
+ }
+
+ pZeroCounterOut[0] = zeroCounter;
+ pRiceParamPartOut[0] = riceParamPart;
+
+ return DRFLAC_TRUE;
+}
+#endif
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_uint32 riceParamPlus1 = riceParam + 1;
+ //drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);
+ drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
+ drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
+
+ // The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have
+ // no idea how this will work in practice...
+ drflac_cache_t bs_cache = bs->cache;
+ drflac_uint32 bs_consumedBits = bs->consumedBits;
+
+ // The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line.
+ drflac_uint32 lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ pZeroCounterOut[0] = lzcount;
+
+ // It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting
+ // this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled
+ // outside of this function at a higher level.
+ extract_rice_param_part:
+ bs_cache <<= lzcount;
+ bs_consumedBits += lzcount;
+
+ if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
+ // Getting here means the rice parameter part is wholly contained within the current cache line.
+ pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+ bs_cache <<= riceParamPlus1;
+ bs_consumedBits += riceParamPlus1;
+ } else {
+ // Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache
+ // line, reload the cache, and then combine it with the head of the next cache line.
+
+ // Grab the high part of the rice parameter part.
+ drflac_uint32 riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+
+ // Before reloading the cache we need to grab the size in bits of the low part.
+ drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
+ drflac_assert(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
+
+ // Now reload the cache.
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = riceParamPartLoBitCount;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
+ }
+
+ // We should now have enough information to construct the rice parameter part.
+ drflac_uint32 riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
+ pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
+
+ bs_cache <<= riceParamPartLoBitCount;
+ }
+ } else {
+ // Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call
+ // to drflac__clz() and we need to reload the cache.
+ drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
+ for (;;) {
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = 0;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits;
+ }
+
+ lzcount = drflac__clz(bs_cache);
+ zeroCounter += lzcount;
+
+ if (lzcount < sizeof(bs_cache)*8) {
+ break;
+ }
+ }
+
+ pZeroCounterOut[0] = zeroCounter;
+ goto extract_rice_param_part;
+ }
+
+ // Make sure the cache is restored at the end of it all.
+ bs->cache = bs_cache;
+ bs->consumedBits = bs_consumedBits;
+
+ return DRFLAC_TRUE;
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x4(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_uint32 riceParamPlus1 = riceParam + 1;
+ //drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);
+ drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
+ drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
+
+ // The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have
+ // no idea how this will work in practice...
+ drflac_cache_t bs_cache = bs->cache;
+ drflac_uint32 bs_consumedBits = bs->consumedBits;
+
+ // What this is doing is trying to efficiently extract 4 rice parts at a time, the idea being that we can exploit certain properties
+ // to our advantage to make things more efficient.
+ for (int i = 0; i < 4; ++i) {
+ // The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line.
+ drflac_uint32 lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ pZeroCounterOut[i] = lzcount;
+
+ // It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting
+ // this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled
+ // outside of this function at a higher level.
+ extract_rice_param_part:
+ bs_cache <<= lzcount;
+ bs_consumedBits += lzcount;
+
+ if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
+ // Getting here means the rice parameter part is wholly contained within the current cache line.
+ pRiceParamPartOut[i] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+ bs_cache <<= riceParamPlus1;
+ bs_consumedBits += riceParamPlus1;
+ } else {
+ // Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache
+ // line, reload the cache, and then combine it with the head of the next cache line.
+
+ // Grab the high part of the rice parameter part.
+ drflac_uint32 riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+
+ // Before reloading the cache we need to grab the size in bits of the low part.
+ drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
+
+ // Now reload the cache.
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = riceParamPartLoBitCount;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
+ }
+
+ // We should now have enough information to construct the rice parameter part.
+ drflac_uint32 riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
+ pRiceParamPartOut[i] = riceParamPartHi | riceParamPartLo;
+
+ bs_cache <<= riceParamPartLoBitCount;
+ }
+ } else {
+ // Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call
+ // to drflac__clz() and we need to reload the cache.
+ drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
+ for (;;) {
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = 0;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits;
+ }
+
+ lzcount = drflac__clz(bs_cache);
+ zeroCounter += lzcount;
+
+ if (lzcount < sizeof(bs_cache)*8) {
+ break;
+ }
+ }
+
+ pZeroCounterOut[i] = zeroCounter;
+ goto extract_rice_param_part;
+ }
+ }
+
+ // Make sure the cache is restored at the end of it all.
+ bs->cache = bs_cache;
+ bs->consumedBits = bs_consumedBits;
+
+ return DRFLAC_TRUE;
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam)
+{
+ drflac_uint32 riceParamPlus1 = riceParam + 1;
+ drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
+
+ // The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have
+ // no idea how this will work in practice...
+ drflac_cache_t bs_cache = bs->cache;
+ drflac_uint32 bs_consumedBits = bs->consumedBits;
+
+ // The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line.
+ drflac_uint32 lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ // It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting
+ // this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled
+ // outside of this function at a higher level.
+ extract_rice_param_part:
+ bs_cache <<= lzcount;
+ bs_consumedBits += lzcount;
+
+ if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
+ // Getting here means the rice parameter part is wholly contained within the current cache line.
+ bs_cache <<= riceParamPlus1;
+ bs_consumedBits += riceParamPlus1;
+ } else {
+ // Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache
+ // line, reload the cache, and then combine it with the head of the next cache line.
+
+ // Before reloading the cache we need to grab the size in bits of the low part.
+ drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
+ drflac_assert(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
+
+ // Now reload the cache.
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = riceParamPartLoBitCount;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
+ }
+
+ bs_cache <<= riceParamPartLoBitCount;
+ }
+ } else {
+ // Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call
+ // to drflac__clz() and we need to reload the cache.
+ for (;;) {
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ #ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+ #endif
+ bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs_consumedBits = 0;
+ #ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs_cache;
+ #endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits;
+ }
+
+ lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ break;
+ }
+ }
+
+ goto extract_rice_param_part;
+ }
+
+ // Make sure the cache is restored at the end of it all.
+ bs->cache = bs_cache;
+ bs->consumedBits = bs_consumedBits;
+
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(pSamplesOut != NULL);
+
+ drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ drflac_uint32 zeroCountPart0;
+ drflac_uint32 zeroCountPart1;
+ drflac_uint32 zeroCountPart2;
+ drflac_uint32 zeroCountPart3;
+ drflac_uint32 riceParamPart0;
+ drflac_uint32 riceParamPart1;
+ drflac_uint32 riceParamPart2;
+ drflac_uint32 riceParamPart3;
+ drflac_uint32 riceParamMask = ~((~0UL) << riceParam);
+ const drflac_int32* pSamplesOutEnd = pSamplesOut + ((count >> 2) << 2);
+
+ if (bitsPerSample >= 24) {
+ while (pSamplesOut < pSamplesOutEnd) {
+ // Rice extraction. It's faster to do this one at a time against local variables than it is to use the x4 version
+ // against an array. Not sure why, but perhaps it's making more efficient use of registers?
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
+ return DRFLAC_FALSE;
+ }
+
+ riceParamPart0 &= riceParamMask;
+ riceParamPart1 &= riceParamMask;
+ riceParamPart2 &= riceParamMask;
+ riceParamPart3 &= riceParamMask;
+
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart1 |= (zeroCountPart1 << riceParam);
+ riceParamPart2 |= (zeroCountPart2 << riceParam);
+ riceParamPart3 |= (zeroCountPart3 << riceParam);
+
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
+ riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
+ riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
+
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3);
+
+ pSamplesOut += 4;
+ }
+ } else {
+ while (pSamplesOut < pSamplesOutEnd) {
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
+ return DRFLAC_FALSE;
+ }
+
+ riceParamPart0 &= riceParamMask;
+ riceParamPart1 &= riceParamMask;
+ riceParamPart2 &= riceParamMask;
+ riceParamPart3 &= riceParamMask;
+
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart1 |= (zeroCountPart1 << riceParam);
+ riceParamPart2 |= (zeroCountPart2 << riceParam);
+ riceParamPart3 |= (zeroCountPart3 << riceParam);
+
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
+ riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
+ riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
+
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
+
+ pSamplesOut += 4;
+ }
+ }
+
+
+
+ drflac_uint32 i = ((count >> 2) << 2);
+ while (i < count) {
+ // Rice extraction.
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Rice reconstruction.
+ riceParamPart0 &= riceParamMask;
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ //riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);
+
+ // Sample reconstruction.
+ if (bitsPerSample >= 24) {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+ } else {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ }
+
+ i += 1;
+ pSamplesOut += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+#if defined(DRFLAC_SUPPORT_SSE41)
+static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(pSamplesOut != NULL);
+
+ static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ //drflac_uint32 zeroCountParts[4];
+ //drflac_uint32 riceParamParts[4];
+
+ drflac_uint32 zeroCountParts0;
+ drflac_uint32 zeroCountParts1;
+ drflac_uint32 zeroCountParts2;
+ drflac_uint32 zeroCountParts3;
+ drflac_uint32 riceParamParts0;
+ drflac_uint32 riceParamParts1;
+ drflac_uint32 riceParamParts2;
+ drflac_uint32 riceParamParts3;
+
+ drflac_uint32 riceParamMask = ~((~0UL) << riceParam);
+ __m128i riceParamMask128 = _mm_set1_epi32(riceParamMask);
+ __m128i one = _mm_set1_epi32(0x01);
+
+ const drflac_int32* pSamplesOutEnd = pSamplesOut + ((count >> 2) << 2);
+
+ if (bitsPerSample >= 24) {
+ while (pSamplesOut < pSamplesOutEnd) {
+ // Rice extraction.
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
+ return DRFLAC_FALSE;
+ }
+
+ __m128i zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
+ __m128i riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
+
+ riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
+ riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
+ riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, one), _mm_set1_epi32(0xFFFFFFFF))); // <-- Only supported from SSE4.1
+ //riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, one)), one)); // <-- SSE2 compatible
+
+ drflac_uint32 riceParamParts[4];
+ _mm_storeu_si128((__m128i*)riceParamParts, riceParamPart128);
+
+ #if defined(DRFLAC_64BIT)
+ // The scalar implementation seems to be faster on 64-bit in my testing.
+ drflac__calculate_prediction_64_x4(order, shift, coefficients, riceParamParts, pSamplesOut);
+ #else
+ pSamplesOut[0] = riceParamParts[0] + drflac__calculate_prediction_64__sse41(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamParts[1] + drflac__calculate_prediction_64__sse41(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamParts[2] + drflac__calculate_prediction_64__sse41(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamParts[3] + drflac__calculate_prediction_64__sse41(order, shift, coefficients, pSamplesOut + 3);
+ #endif
+
+ pSamplesOut += 4;
+ }
+ } else {
+ drflac_int32 coefficientsUnaligned[32*4 + 4] = {0};
+ drflac_int32* coefficients128 = (drflac_int32*)(((size_t)coefficientsUnaligned + 15) & ~15);
+ for (drflac_uint32 i = 0; i < order; ++i) {
+ coefficients128[i*4+0] = coefficients[i];
+ coefficients128[i*4+1] = coefficients[i];
+ coefficients128[i*4+2] = coefficients[i];
+ coefficients128[i*4+3] = coefficients[i];
+ }
+
+ while (pSamplesOut < pSamplesOutEnd) {
+ // Rice extraction.
+#if 1
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
+ return DRFLAC_FALSE;
+ }
+
+ __m128i zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
+ __m128i riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
+#else
+ if (!drflac__read_rice_parts_x4(bs, riceParam, zeroCountParts, riceParamParts)) {
+ return DRFLAC_FALSE;
+ }
+
+ __m128i zeroCountPart128 = _mm_set_epi32(zeroCountParts[3], zeroCountParts[2], zeroCountParts[1], zeroCountParts[0]);
+ __m128i riceParamPart128 = _mm_set_epi32(riceParamParts[3], riceParamParts[2], riceParamParts[1], riceParamParts[0]);
+#endif
+
+ riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
+ riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
+ riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, one), _mm_set1_epi32(0xFFFFFFFF)));
+
+#if 1
+ drflac__calculate_prediction_32_x4__sse41(order, shift, (const __m128i*)coefficients128, riceParamPart128, pSamplesOut);
+#else
+ drflac_int32 riceParamParts[4];
+ _mm_storeu_si128((__m128i*)riceParamParts, riceParamPart128);
+
+ pSamplesOut[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamParts[1] + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamParts[2] + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamParts[3] + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
+#endif
+
+ pSamplesOut += 4;
+ }
+ }
+
+
+ drflac_uint32 i = ((count >> 2) << 2);
+ while (i < count) {
+ // Rice extraction.
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Rice reconstruction.
+ riceParamParts0 &= riceParamMask;
+ riceParamParts0 |= (zeroCountParts0 << riceParam);
+ riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
+
+ // Sample reconstruction.
+ if (bitsPerSample >= 24) {
+ pSamplesOut[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+ } else {
+ pSamplesOut[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ }
+
+ i += 1;
+ pSamplesOut += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+#endif
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+#if defined(DRFLAC_SUPPORT_SSE41)
+ if (drflac__gIsSSE41Supported) {
+ return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ } else
+#endif
+ {
+ // Scalar fallback.
+ #if 0
+ return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ #else
+ return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ #endif
+ }
+}
+
+// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes.
+static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+
+ for (drflac_uint32 i = 0; i < count; ++i) {
+ if (!drflac__seek_rice_parts(bs, riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(unencodedBitsPerSample <= 31); // <-- unencodedBitsPerSample is a 5 bit number, so cannot exceed 31.
+ drflac_assert(pSamplesOut != NULL);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ if (unencodedBitsPerSample > 0) {
+ if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ pSamplesOut[i] = 0;
+ }
+
+ if (bitsPerSample > 16) {
+ pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
+ } else {
+ pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+
+// Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called
+// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The
+// and parameters are used to determine how many residual values need to be decoded.
+static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(blockSize != 0);
+ drflac_assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode?
+
+ drflac_uint8 residualMethod;
+ if (!drflac__read_uint8(bs, 2, &residualMethod)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ return DRFLAC_FALSE; // Unknown or unsupported residual coding method.
+ }
+
+ // Ignore the first values.
+ pDecodedSamples += order;
+
+
+ drflac_uint8 partitionOrder;
+ if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
+ return DRFLAC_FALSE;
+ }
+
+ // From the FLAC spec:
+ // The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
+ if (partitionOrder > 8) {
+ return DRFLAC_FALSE;
+ }
+
+ // Validation check.
+ if ((blockSize / (1 << partitionOrder)) <= order) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ drflac_uint32 partitionsRemaining = (1 << partitionOrder);
+ for (;;) {
+ drflac_uint8 riceParam = 0;
+ if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
+ if (!drflac__read_uint8(bs, 4, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 15) {
+ riceParam = 0xFF;
+ }
+ } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ if (!drflac__read_uint8(bs, 5, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 31) {
+ riceParam = 0xFF;
+ }
+ }
+
+ if (riceParam != 0xFF) {
+ if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ unsigned char unencodedBitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ pDecodedSamples += samplesInPartition;
+
+
+ if (partitionsRemaining == 1) {
+ break;
+ }
+
+ partitionsRemaining -= 1;
+
+ if (partitionOrder != 0) {
+ samplesInPartition = blockSize / (1 << partitionOrder);
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+// Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called
+// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The
+// and parameters are used to determine how many residual values need to be decoded.
+static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(blockSize != 0);
+
+ drflac_uint8 residualMethod;
+ if (!drflac__read_uint8(bs, 2, &residualMethod)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ return DRFLAC_FALSE; // Unknown or unsupported residual coding method.
+ }
+
+ drflac_uint8 partitionOrder;
+ if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
+ return DRFLAC_FALSE;
+ }
+
+ // From the FLAC spec:
+ // The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
+ if (partitionOrder > 8) {
+ return DRFLAC_FALSE;
+ }
+
+ // Validation check.
+ if ((blockSize / (1 << partitionOrder)) <= order) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ drflac_uint32 partitionsRemaining = (1 << partitionOrder);
+ for (;;)
+ {
+ drflac_uint8 riceParam = 0;
+ if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
+ if (!drflac__read_uint8(bs, 4, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 15) {
+ riceParam = 0xFF;
+ }
+ } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ if (!drflac__read_uint8(bs, 5, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 31) {
+ riceParam = 0xFF;
+ }
+ }
+
+ if (riceParam != 0xFF) {
+ if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ unsigned char unencodedBitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+
+ if (partitionsRemaining == 1) {
+ break;
+ }
+
+ partitionsRemaining -= 1;
+ samplesInPartition = blockSize / (1 << partitionOrder);
+ }
+
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples)
+{
+ // Only a single sample needs to be decoded here.
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ // We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely)
+ // we'll want to look at a more efficient way.
+ for (drflac_uint32 i = 0; i < blockSize; ++i) {
+ pDecodedSamples[i] = sample;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples)
+{
+ for (drflac_uint32 i = 0; i < blockSize; ++i) {
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
+{
+ static drflac_int32 lpcCoefficientsTable[5][4] = {
+ {0, 0, 0, 0},
+ {1, 0, 0, 0},
+ {2, -1, 0, 0},
+ {3, -3, 1, 0},
+ {4, -6, 4, -1}
+ };
+
+ // Warm up samples and coefficients.
+ for (drflac_uint32 i = 0; i < lpcOrder; ++i) {
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+
+ if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
+ return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
+{
+ drflac_uint8 i;
+
+ // Warm up samples.
+ for (i = 0; i < lpcOrder; ++i) {
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+ drflac_uint8 lpcPrecision;
+ if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
+ return DRFLAC_FALSE;
+ }
+ if (lpcPrecision == 15) {
+ return DRFLAC_FALSE; // Invalid.
+ }
+ lpcPrecision += 1;
+
+
+ drflac_int8 lpcShift;
+ if (!drflac__read_int8(bs, 5, &lpcShift)) {
+ return DRFLAC_FALSE;
+ }
+
+
+ drflac_int32 coefficients[32];
+ drflac_zero_memory(coefficients, sizeof(coefficients));
+ for (i = 0; i < lpcOrder; ++i) {
+ if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) {
+ return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(header != NULL);
+
+ const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
+ const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; // -1 = reserved.
+
+ // Keep looping until we find a valid sync code.
+ for (;;) {
+ if (!drflac__find_and_seek_to_next_sync_code(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint8 crc8 = 0xCE; // 0xCE = drflac_crc8(0, 0x3FFE, 14);
+
+ drflac_uint8 reserved = 0;
+ if (!drflac__read_uint8(bs, 1, &reserved)) {
+ return DRFLAC_FALSE;
+ }
+ if (reserved == 1) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, reserved, 1);
+
+
+ drflac_uint8 blockingStrategy = 0;
+ if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, blockingStrategy, 1);
+
+
+ drflac_uint8 blockSize = 0;
+ if (!drflac__read_uint8(bs, 4, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ if (blockSize == 0) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, blockSize, 4);
+
+
+ drflac_uint8 sampleRate = 0;
+ if (!drflac__read_uint8(bs, 4, &sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, sampleRate, 4);
+
+
+ drflac_uint8 channelAssignment = 0;
+ if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
+ return DRFLAC_FALSE;
+ }
+ if (channelAssignment > 10) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, channelAssignment, 4);
+
+
+ drflac_uint8 bitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+ if (bitsPerSample == 3 || bitsPerSample == 7) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, bitsPerSample, 3);
+
+
+ if (!drflac__read_uint8(bs, 1, &reserved)) {
+ return DRFLAC_FALSE;
+ }
+ if (reserved == 1) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, reserved, 1);
+
+
+ drflac_bool32 isVariableBlockSize = blockingStrategy == 1;
+ if (isVariableBlockSize) {
+ drflac_uint64 sampleNumber;
+ drflac_result result = drflac__read_utf8_coded_number(bs, &sampleNumber, &crc8);
+ if (result != DRFLAC_SUCCESS) {
+ if (result == DRFLAC_END_OF_STREAM) {
+ return DRFLAC_FALSE;
+ } else {
+ continue;
+ }
+ }
+ header->frameNumber = 0;
+ header->sampleNumber = sampleNumber;
+ } else {
+ drflac_uint64 frameNumber = 0;
+ drflac_result result = drflac__read_utf8_coded_number(bs, &frameNumber, &crc8);
+ if (result != DRFLAC_SUCCESS) {
+ if (result == DRFLAC_END_OF_STREAM) {
+ return DRFLAC_FALSE;
+ } else {
+ continue;
+ }
+ }
+ header->frameNumber = (drflac_uint32)frameNumber; // <-- Safe cast.
+ header->sampleNumber = 0;
+ }
+
+
+ if (blockSize == 1) {
+ header->blockSize = 192;
+ } else if (blockSize >= 2 && blockSize <= 5) {
+ header->blockSize = 576 * (1 << (blockSize - 2));
+ } else if (blockSize == 6) {
+ if (!drflac__read_uint16(bs, 8, &header->blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->blockSize, 8);
+ header->blockSize += 1;
+ } else if (blockSize == 7) {
+ if (!drflac__read_uint16(bs, 16, &header->blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->blockSize, 16);
+ header->blockSize += 1;
+ } else {
+ header->blockSize = 256 * (1 << (blockSize - 8));
+ }
+
+
+ if (sampleRate <= 11) {
+ header->sampleRate = sampleRateTable[sampleRate];
+ } else if (sampleRate == 12) {
+ if (!drflac__read_uint32(bs, 8, &header->sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->sampleRate, 8);
+ header->sampleRate *= 1000;
+ } else if (sampleRate == 13) {
+ if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->sampleRate, 16);
+ } else if (sampleRate == 14) {
+ if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->sampleRate, 16);
+ header->sampleRate *= 10;
+ } else {
+ continue; // Invalid. Assume an invalid block.
+ }
+
+
+ header->channelAssignment = channelAssignment;
+
+ header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
+ if (header->bitsPerSample == 0) {
+ header->bitsPerSample = streaminfoBitsPerSample;
+ }
+
+ if (!drflac__read_uint8(bs, 8, &header->crc8)) {
+ return DRFLAC_FALSE;
+ }
+
+#ifndef DR_FLAC_NO_CRC
+ if (header->crc8 != crc8) {
+ continue; // CRC mismatch. Loop back to the top and find the next sync code.
+ }
+#endif
+ return DRFLAC_TRUE;
+ }
+}
+
+static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe)
+{
+ drflac_uint8 header;
+ if (!drflac__read_uint8(bs, 8, &header)) {
+ return DRFLAC_FALSE;
+ }
+
+ // First bit should always be 0.
+ if ((header & 0x80) != 0) {
+ return DRFLAC_FALSE;
+ }
+
+ int type = (header & 0x7E) >> 1;
+ if (type == 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
+ } else if (type == 1) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM;
+ } else {
+ if ((type & 0x20) != 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_LPC;
+ pSubframe->lpcOrder = (type & 0x1F) + 1;
+ } else if ((type & 0x08) != 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED;
+ pSubframe->lpcOrder = (type & 0x07);
+ if (pSubframe->lpcOrder > 4) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
+ pSubframe->lpcOrder = 0;
+ }
+ } else {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
+ }
+ }
+
+ if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) {
+ return DRFLAC_FALSE;
+ }
+
+ // Wasted bits per sample.
+ pSubframe->wastedBitsPerSample = 0;
+ if ((header & 0x01) == 1) {
+ unsigned int wastedBitsPerSample;
+ if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+ pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(frame != NULL);
+
+ drflac_subframe* pSubframe = frame->subframes + subframeIndex;
+ if (!drflac__read_subframe_header(bs, pSubframe)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Side channels require an extra bit per sample. Took a while to figure that one out...
+ pSubframe->bitsPerSample = frame->header.bitsPerSample;
+ if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
+ pSubframe->bitsPerSample += 1;
+ } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
+ pSubframe->bitsPerSample += 1;
+ }
+
+ // Need to handle wasted bits per sample.
+ if (pSubframe->wastedBitsPerSample >= pSubframe->bitsPerSample) {
+ return DRFLAC_FALSE;
+ }
+ pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample;
+ pSubframe->pDecodedSamples = pDecodedSamplesOut;
+
+ switch (pSubframe->subframeType)
+ {
+ case DRFLAC_SUBFRAME_CONSTANT:
+ {
+ drflac__decode_samples__constant(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_VERBATIM:
+ {
+ drflac__decode_samples__verbatim(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_FIXED:
+ {
+ drflac__decode_samples__fixed(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_LPC:
+ {
+ drflac__decode_samples__lpc(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples);
+ } break;
+
+ default: return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(frame != NULL);
+
+ drflac_subframe* pSubframe = frame->subframes + subframeIndex;
+ if (!drflac__read_subframe_header(bs, pSubframe)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Side channels require an extra bit per sample. Took a while to figure that one out...
+ pSubframe->bitsPerSample = frame->header.bitsPerSample;
+ if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
+ pSubframe->bitsPerSample += 1;
+ } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
+ pSubframe->bitsPerSample += 1;
+ }
+
+ // Need to handle wasted bits per sample.
+ if (pSubframe->wastedBitsPerSample >= pSubframe->bitsPerSample) {
+ return DRFLAC_FALSE;
+ }
+ pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample;
+ pSubframe->pDecodedSamples = NULL;
+
+ switch (pSubframe->subframeType)
+ {
+ case DRFLAC_SUBFRAME_CONSTANT:
+ {
+ if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_VERBATIM:
+ {
+ unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_FIXED:
+ {
+ unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_LPC:
+ {
+ unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+
+ unsigned char lpcPrecision;
+ if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
+ return DRFLAC_FALSE;
+ }
+ if (lpcPrecision == 15) {
+ return DRFLAC_FALSE; // Invalid.
+ }
+ lpcPrecision += 1;
+
+
+ bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift.
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ default: return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+
+static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment)
+{
+ drflac_assert(channelAssignment <= 10);
+
+ drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
+ return lookup[channelAssignment];
+}
+
+static drflac_result drflac__decode_flac_frame(drflac* pFlac)
+{
+ // This function should be called while the stream is sitting on the first byte after the frame header.
+ drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes));
+
+ // The frame block size must never be larger than the maximum block size defined by the FLAC stream.
+ if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) {
+ return DRFLAC_ERROR;
+ }
+
+ // The number of channels in the frame must match the channel count from the STREAMINFO block.
+ int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ if (channelCount != (int)pFlac->channels) {
+ return DRFLAC_ERROR;
+ }
+
+ for (int i = 0; i < channelCount; ++i) {
+ if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + ((pFlac->currentFrame.header.blockSize+DRFLAC_LEADING_SAMPLES) * i) + DRFLAC_LEADING_SAMPLES)) {
+ return DRFLAC_ERROR;
+ }
+ }
+
+ drflac_uint8 paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7;
+ if (paddingSizeInBits > 0) {
+ drflac_uint8 padding = 0;
+ if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ }
+
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
+#endif
+ drflac_uint16 desiredCRC16;
+ if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
+ return DRFLAC_END_OF_STREAM;
+ }
+
+#ifndef DR_FLAC_NO_CRC
+ if (actualCRC16 != desiredCRC16) {
+ return DRFLAC_CRC_MISMATCH; // CRC mismatch.
+ }
+#endif
+
+ pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount;
+
+ return DRFLAC_SUCCESS;
+}
+
+static drflac_result drflac__seek_flac_frame(drflac* pFlac)
+{
+ int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ for (int i = 0; i < channelCount; ++i) {
+ if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) {
+ return DRFLAC_ERROR;
+ }
+ }
+
+ // Padding.
+ if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
+ return DRFLAC_ERROR;
+ }
+
+ // CRC.
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
+#endif
+ drflac_uint16 desiredCRC16;
+ if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
+ return DRFLAC_END_OF_STREAM;
+ }
+
+#ifndef DR_FLAC_NO_CRC
+ if (actualCRC16 != desiredCRC16) {
+ return DRFLAC_CRC_MISMATCH; // CRC mismatch.
+ }
+#endif
+
+ return DRFLAC_SUCCESS;
+}
+
+static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac)
+{
+ drflac_assert(pFlac != NULL);
+
+ for (;;) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_result result = drflac__decode_flac_frame(pFlac);
+ if (result != DRFLAC_SUCCESS) {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ continue; // CRC mismatch. Skip to the next frame.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ return DRFLAC_TRUE;
+ }
+}
+
+
+static void drflac__get_current_frame_sample_range(drflac* pFlac, drflac_uint64* pFirstSampleInFrameOut, drflac_uint64* pLastSampleInFrameOut)
+{
+ drflac_assert(pFlac != NULL);
+
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+
+ drflac_uint64 firstSampleInFrame = pFlac->currentFrame.header.sampleNumber;
+ if (firstSampleInFrame == 0) {
+ firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount;
+ }
+
+ drflac_uint64 lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount);
+ if (lastSampleInFrame > 0) {
+ lastSampleInFrame -= 1; // Needs to be zero based.
+ }
+
+ if (pFirstSampleInFrameOut) *pFirstSampleInFrameOut = firstSampleInFrame;
+ if (pLastSampleInFrameOut) *pLastSampleInFrameOut = lastSampleInFrame;
+}
+
+static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
+{
+ drflac_assert(pFlac != NULL);
+
+ drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos);
+
+ drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame));
+ pFlac->currentSample = 0;
+
+ return result;
+}
+
+static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac)
+{
+ // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section.
+ drflac_assert(pFlac != NULL);
+ return drflac__seek_flac_frame(pFlac);
+}
+
+drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 samplesToRead)
+{
+ drflac_uint64 samplesRead = 0;
+ while (samplesToRead > 0) {
+ if (pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; // Couldn't read the next frame, so just break from the loop and return.
+ }
+ } else {
+ if (pFlac->currentFrame.samplesRemaining > samplesToRead) {
+ samplesRead += samplesToRead;
+ pFlac->currentFrame.samplesRemaining -= (drflac_uint32)samplesToRead; // <-- Safe cast. Will always be < currentFrame.samplesRemaining < 65536.
+ samplesToRead = 0;
+ } else {
+ samplesRead += pFlac->currentFrame.samplesRemaining;
+ samplesToRead -= pFlac->currentFrame.samplesRemaining;
+ pFlac->currentFrame.samplesRemaining = 0;
+ }
+ }
+ }
+
+ pFlac->currentSample += samplesRead;
+ return samplesRead;
+}
+
+drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek)
+{
+ return drflac__seek_forward_by_samples(pFlac, pcmFramesToSeek*pFlac->channels);
+}
+
+static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ drflac_assert(pFlac != NULL);
+
+ drflac_bool32 isMidFrame = DRFLAC_FALSE;
+
+ // If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file.
+ drflac_uint64 runningSampleCount;
+ if (sampleIndex >= pFlac->currentSample) {
+ // Seeking forward. Need to seek from the current position.
+ runningSampleCount = pFlac->currentSample;
+
+ // The frame header for the first frame may not yet have been read. We need to do that if necessary.
+ if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ isMidFrame = DRFLAC_TRUE;
+ }
+ } else {
+ // Seeking backwards. Need to seek from the start of the file.
+ runningSampleCount = 0;
+
+ // Move back to the start.
+ if (!drflac__seek_to_first_frame(pFlac)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Decode the first frame in preparation for sample-exact seeking below.
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ // We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its
+ // header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame.
+ for (;;) {
+ drflac_uint64 firstSampleInFrame = 0;
+ drflac_uint64 lastSampleInFrame = 0;
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
+
+ drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
+ // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend
+ // it never existed and keep iterating.
+ drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount;
+
+ if (!isMidFrame) {
+ drflac_result result = drflac__decode_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail).
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // We started seeking mid-frame which means we need to skip the frame decoding part.
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode;
+ }
+ } else {
+ // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ // frame never existed and leave the running sample count untouched.
+ if (!isMidFrame) {
+ drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningSampleCount += sampleCountInThisFrame;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with
+ // drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ runningSampleCount += pFlac->currentFrame.samplesRemaining;
+ pFlac->currentFrame.samplesRemaining = 0;
+ isMidFrame = DRFLAC_FALSE;
+ }
+ }
+
+ next_iteration:
+ // Grab the next frame in preparation for the next iteration.
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+}
+
+
+static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ drflac_assert(pFlac != NULL);
+
+ if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
+ return DRFLAC_FALSE;
+ }
+
+
+ drflac_uint32 iClosestSeekpoint = 0;
+ for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
+ if (pFlac->pSeekpoints[iSeekpoint].firstSample*pFlac->channels >= sampleIndex) {
+ break;
+ }
+
+ iClosestSeekpoint = iSeekpoint;
+ }
+
+
+ drflac_bool32 isMidFrame = DRFLAC_FALSE;
+
+ // At this point we should have found the seekpoint closest to our sample. If we are seeking forward and the closest seekpoint is _before_ the current sample, we
+ // just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample.
+ drflac_uint64 runningSampleCount;
+ if ((sampleIndex >= pFlac->currentSample) && (pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels <= pFlac->currentSample)) {
+ // Optimized case. Just seek forward from where we are.
+ runningSampleCount = pFlac->currentSample;
+
+ // The frame header for the first frame may not yet have been read. We need to do that if necessary.
+ if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ isMidFrame = DRFLAC_TRUE;
+ }
+ } else {
+ // Slower case. Seek to the start of the seekpoint and then seek forward from there.
+ runningSampleCount = pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels;
+
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + pFlac->pSeekpoints[iClosestSeekpoint].frameOffset)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below.
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ for (;;) {
+ drflac_uint64 firstSampleInFrame = 0;
+ drflac_uint64 lastSampleInFrame = 0;
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
+
+ drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
+ // The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend
+ // it never existed and keep iterating.
+ drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount;
+
+ if (!isMidFrame) {
+ drflac_result result = drflac__decode_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail).
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // We started seeking mid-frame which means we need to skip the frame decoding part.
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode;
+ }
+ } else {
+ // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ // frame never existed and leave the running sample count untouched.
+ if (!isMidFrame) {
+ drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningSampleCount += sampleCountInThisFrame;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with
+ // drflac__seek_to_next_flac_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ runningSampleCount += pFlac->currentFrame.samplesRemaining;
+ pFlac->currentFrame.samplesRemaining = 0;
+ isMidFrame = DRFLAC_FALSE;
+ }
+ }
+
+ next_iteration:
+ // Grab the next frame in preparation for the next iteration.
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+}
+
+
+#ifndef DR_FLAC_NO_OGG
+typedef struct
+{
+ drflac_uint8 capturePattern[4]; // Should be "OggS"
+ drflac_uint8 structureVersion; // Always 0.
+ drflac_uint8 headerType;
+ drflac_uint64 granulePosition;
+ drflac_uint32 serialNumber;
+ drflac_uint32 sequenceNumber;
+ drflac_uint32 checksum;
+ drflac_uint8 segmentCount;
+ drflac_uint8 segmentTable[255];
+} drflac_ogg_page_header;
+#endif
+
+typedef struct
+{
+ drflac_read_proc onRead;
+ drflac_seek_proc onSeek;
+ drflac_meta_proc onMeta;
+ drflac_container container;
+ void* pUserData;
+ void* pUserDataMD;
+ drflac_uint32 sampleRate;
+ drflac_uint8 channels;
+ drflac_uint8 bitsPerSample;
+ drflac_uint64 totalSampleCount;
+ drflac_uint16 maxBlockSize;
+ drflac_uint64 runningFilePos;
+ drflac_bool32 hasStreamInfoBlock;
+ drflac_bool32 hasMetadataBlocks;
+ drflac_bs bs; // <-- A bit streamer is required for loading data during initialization.
+ drflac_frame_header firstFrameHeader; // <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block.
+
+#ifndef DR_FLAC_NO_OGG
+ drflac_uint32 oggSerial;
+ drflac_uint64 oggFirstBytePos;
+ drflac_ogg_page_header oggBosHeader;
+#endif
+} drflac_init_info;
+
+static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
+{
+ blockHeader = drflac__be2host_32(blockHeader);
+ *isLastBlock = (blockHeader & (0x01 << 31)) >> 31;
+ *blockType = (blockHeader & (0x7F << 24)) >> 24;
+ *blockSize = (blockHeader & 0xFFFFFF);
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
+{
+ drflac_uint32 blockHeader;
+ if (onRead(pUserData, &blockHeader, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
+ return DRFLAC_TRUE;
+}
+
+drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo)
+{
+ // min/max block size.
+ drflac_uint32 blockSizes;
+ if (onRead(pUserData, &blockSizes, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ // min/max frame size.
+ drflac_uint64 frameSizes = 0;
+ if (onRead(pUserData, &frameSizes, 6) != 6) {
+ return DRFLAC_FALSE;
+ }
+
+ // Sample rate, channels, bits per sample and total sample count.
+ drflac_uint64 importantProps;
+ if (onRead(pUserData, &importantProps, 8) != 8) {
+ return DRFLAC_FALSE;
+ }
+
+ // MD5
+ drflac_uint8 md5[16];
+ if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
+ return DRFLAC_FALSE;
+ }
+
+ blockSizes = drflac__be2host_32(blockSizes);
+ frameSizes = drflac__be2host_64(frameSizes);
+ importantProps = drflac__be2host_64(importantProps);
+
+ pStreamInfo->minBlockSize = (blockSizes & 0xFFFF0000) >> 16;
+ pStreamInfo->maxBlockSize = blockSizes & 0x0000FFFF;
+ pStreamInfo->minFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0xFFFFFF0000000000) >> 40);
+ pStreamInfo->maxFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0x000000FFFFFF0000) >> 16);
+ pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (drflac_uint64)0xFFFFF00000000000) >> 44);
+ pStreamInfo->channels = (drflac_uint8 )((importantProps & (drflac_uint64)0x00000E0000000000) >> 41) + 1;
+ pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (drflac_uint64)0x000001F000000000) >> 36) + 1;
+ pStreamInfo->totalSampleCount = (importantProps & (drflac_uint64)0x0000000FFFFFFFFF) * pStreamInfo->channels;
+ drflac_copy_memory(pStreamInfo->md5, md5, sizeof(md5));
+
+ return DRFLAC_TRUE;
+}
+
+drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize)
+{
+ // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that
+ // we'll be sitting on byte 42.
+ drflac_uint64 runningFilePos = 42;
+ drflac_uint64 seektablePos = 0;
+ drflac_uint32 seektableSize = 0;
+
+ for (;;) {
+ drflac_uint8 isLastBlock = 0;
+ drflac_uint8 blockType;
+ drflac_uint32 blockSize;
+ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ runningFilePos += 4;
+
+
+ drflac_metadata metadata;
+ metadata.type = blockType;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+
+ switch (blockType)
+ {
+ case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
+ {
+ if (blockSize < 4) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData);
+ metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32));
+ metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32);
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
+ {
+ seektablePos = runningFilePos;
+ seektableSize = blockSize;
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint);
+ metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData;
+
+ // Endian swap.
+ for (drflac_uint32 iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
+ drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
+ pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample);
+ pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset);
+ pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount);
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
+ {
+ if (blockSize < 8) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ const char* pRunningData = (const char*)pRawData;
+ const char* const pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ // Need space for the rest of the block
+ if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
+ metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ // Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment
+ if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.vorbis_comment.pComments = pRunningData;
+
+ // Check that the comments section is valid before passing it to the callback
+ for (drflac_uint32 i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
+ if (pRunningDataEnd - pRunningData < 4) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ const drflac_uint32 commentLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ pRunningData += commentLength;
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
+ {
+ if (blockSize < 396) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ char* pRunningData = (char*)pRawData;
+ const char* const pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
+ metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8;
+ metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
+ metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
+ metadata.data.cuesheet.pTrackData = pRunningData;
+
+ // Check that the cuesheet tracks are valid before passing it to the callback
+ for (drflac_uint8 i = 0; i < metadata.data.cuesheet.trackCount; ++i) {
+ if (pRunningDataEnd - pRunningData < 36) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ // Skip to the index point count
+ pRunningData += 35;
+ const drflac_uint8 indexCount = pRunningData[0]; pRunningData += 1;
+ const drflac_uint32 indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index);
+ if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ // Endian swap.
+ for (drflac_uint8 index = 0; index < indexCount; ++index) {
+ drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData;
+ pRunningData += sizeof(drflac_cuesheet_track_index);
+ pTrack->offset = drflac__be2host_64(pTrack->offset);
+ }
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
+ {
+ if (blockSize < 32) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ const char* pRunningData = (const char*)pRawData;
+ const char* const pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ metadata.data.picture.type = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.mimeLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ // Need space for the rest of the block
+ if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
+ metadata.data.picture.descriptionLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ // Need space for the rest of the block
+ if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
+ metadata.data.picture.width = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.height = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.colorDepth = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.indexColorCount = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pictureDataSize = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
+
+ // Need space for the picture after the block
+ if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_PADDING:
+ {
+ if (onMeta) {
+ metadata.data.padding.unused = 0;
+
+ // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback.
+ if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+ isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop.
+ } else {
+ onMeta(pUserDataMD, &metadata);
+ }
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_INVALID:
+ {
+ // Invalid chunk. Just skip over this one.
+ if (onMeta) {
+ if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+ isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop.
+ }
+ }
+ } break;
+
+ default:
+ {
+ // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we
+ // can at the very least report the chunk to the application and let it look at the raw data.
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+ }
+
+ // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above.
+ if (onMeta == NULL && blockSize > 0) {
+ if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+ isLastBlock = DRFLAC_TRUE;
+ }
+ }
+
+ runningFilePos += blockSize;
+ if (isLastBlock) {
+ break;
+ }
+ }
+
+ *pSeektablePos = seektablePos;
+ *pSeektableSize = seektableSize;
+ *pFirstFramePos = runningFilePos;
+
+ return DRFLAC_TRUE;
+}
+
+drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
+{
+ (void)onSeek;
+
+ // Pre: The bit stream should be sitting just past the 4-byte id header.
+
+ pInit->container = drflac_container_native;
+
+ // The first metadata block should be the STREAMINFO block.
+ drflac_uint8 isLastBlock;
+ drflac_uint8 blockType;
+ drflac_uint32 blockSize;
+ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
+ if (!relaxed) {
+ // We're opening in strict mode and the first block is not the STREAMINFO block. Error.
+ return DRFLAC_FALSE;
+ } else {
+ // Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined
+ // for that frame.
+ pInit->hasStreamInfoBlock = DRFLAC_FALSE;
+ pInit->hasMetadataBlocks = DRFLAC_FALSE;
+
+ if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
+ return DRFLAC_FALSE; // Couldn't find a frame.
+ }
+
+ if (pInit->firstFrameHeader.bitsPerSample == 0) {
+ return DRFLAC_FALSE; // Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist.
+ }
+
+ pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
+ pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
+ pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
+ pInit->maxBlockSize = 65535; // <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo
+ return DRFLAC_TRUE;
+ }
+ } else {
+ drflac_streaminfo streaminfo;
+ if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
+ return DRFLAC_FALSE;
+ }
+
+ pInit->hasStreamInfoBlock = DRFLAC_TRUE;
+ pInit->sampleRate = streaminfo.sampleRate;
+ pInit->channels = streaminfo.channels;
+ pInit->bitsPerSample = streaminfo.bitsPerSample;
+ pInit->totalSampleCount = streaminfo.totalSampleCount;
+ pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation).
+ pInit->hasMetadataBlocks = !isLastBlock;
+
+ if (onMeta) {
+ drflac_metadata metadata;
+ metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+ metadata.data.streaminfo = streaminfo;
+ onMeta(pUserDataMD, &metadata);
+ }
+
+ return DRFLAC_TRUE;
+ }
+}
+
+#ifndef DR_FLAC_NO_OGG
+#define DRFLAC_OGG_MAX_PAGE_SIZE 65307
+#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 // CRC-32 of "OggS".
+
+typedef enum
+{
+ drflac_ogg_recover_on_crc_mismatch,
+ drflac_ogg_fail_on_crc_mismatch
+} drflac_ogg_crc_mismatch_recovery;
+
+
+static drflac_uint32 drflac__crc32_table[] = {
+ 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
+ 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
+ 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
+ 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
+ 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
+ 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
+ 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
+ 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
+ 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
+ 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
+ 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
+ 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
+ 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
+ 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
+ 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
+ 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
+ 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
+ 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
+ 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
+ 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
+ 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
+ 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
+ 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
+ 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
+ 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
+ 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
+ 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
+ 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
+ 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
+ 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
+ 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
+ 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
+ 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
+ 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
+ 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
+ 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
+ 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
+ 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
+ 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
+ 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
+ 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
+ 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
+ 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
+ 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
+ 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
+ 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
+ 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
+ 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
+ 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
+ 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
+ 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
+ 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
+ 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
+ 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
+ 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
+ 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
+ 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
+ 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
+ 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
+ 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
+ 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
+ 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
+ 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
+ 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
+};
+
+static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data)
+{
+#ifndef DR_FLAC_NO_CRC
+ return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data];
+#else
+ (void)data;
+ return crc32;
+#endif
+}
+
+#if 0
+static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data)
+{
+ crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF));
+ crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF));
+ crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF));
+ crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF));
+ return crc32;
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data)
+{
+ crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF));
+ crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF));
+ return crc32;
+}
+#endif
+
+static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize)
+{
+ // This can be optimized.
+ for (drflac_uint32 i = 0; i < dataSize; ++i) {
+ crc32 = drflac_crc32_byte(crc32, pData[i]);
+ }
+ return crc32;
+}
+
+
+static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4])
+{
+ return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader)
+{
+ return 27 + pHeader->segmentCount;
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader)
+{
+ drflac_uint32 pageBodySize = 0;
+ for (int i = 0; i < pHeader->segmentCount; ++i) {
+ pageBodySize += pHeader->segmentTable[i];
+ }
+
+ return pageBodySize;
+}
+
+drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
+{
+ drflac_assert(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
+
+ drflac_uint8 data[23];
+ if (onRead(pUserData, data, 23) != 23) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ *pBytesRead += 23;
+
+ pHeader->structureVersion = data[0];
+ pHeader->headerType = data[1];
+ drflac_copy_memory(&pHeader->granulePosition, &data[ 2], 8);
+ drflac_copy_memory(&pHeader->serialNumber, &data[10], 4);
+ drflac_copy_memory(&pHeader->sequenceNumber, &data[14], 4);
+ drflac_copy_memory(&pHeader->checksum, &data[18], 4);
+ pHeader->segmentCount = data[22];
+
+ // Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0.
+ data[18] = 0;
+ data[19] = 0;
+ data[20] = 0;
+ data[21] = 0;
+
+ drflac_uint32 i;
+ for (i = 0; i < 23; ++i) {
+ *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]);
+ }
+
+
+ if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ *pBytesRead += pHeader->segmentCount;
+
+ for (i = 0; i < pHeader->segmentCount; ++i) {
+ *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
+ }
+
+ return DRFLAC_SUCCESS;
+}
+
+drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
+{
+ *pBytesRead = 0;
+
+ drflac_uint8 id[4];
+ if (onRead(pUserData, id, 4) != 4) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ *pBytesRead += 4;
+
+ // We need to read byte-by-byte until we find the OggS capture pattern.
+ for (;;) {
+ if (drflac_ogg__is_capture_pattern(id)) {
+ *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
+
+ drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
+ if (result == DRFLAC_SUCCESS) {
+ return DRFLAC_SUCCESS;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ continue;
+ } else {
+ return result;
+ }
+ }
+ } else {
+ // The first 4 bytes did not equal the capture pattern. Read the next byte and try again.
+ id[0] = id[1];
+ id[1] = id[2];
+ id[2] = id[3];
+ if (onRead(pUserData, &id[3], 1) != 1) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ *pBytesRead += 1;
+ }
+ }
+}
+
+
+// The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works
+// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed
+// in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type
+// dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from
+// the physical Ogg bitstream are converted and delivered in native FLAC format.
+typedef struct
+{
+ drflac_read_proc onRead; // The original onRead callback from drflac_open() and family.
+ drflac_seek_proc onSeek; // The original onSeek callback from drflac_open() and family.
+ void* pUserData; // The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family.
+ drflac_uint64 currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking.
+ drflac_uint64 firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page.
+ drflac_uint32 serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization.
+ drflac_ogg_page_header bosPageHeader; // Used for seeking.
+ drflac_ogg_page_header currentPageHeader;
+ drflac_uint32 bytesRemainingInPage;
+ drflac_uint32 pageDataSize;
+ drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE];
+} drflac_oggbs; // oggbs = Ogg Bitstream
+
+static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
+{
+ size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
+ oggbs->currentBytePos += bytesActuallyRead;
+
+ return bytesActuallyRead;
+}
+
+static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin)
+{
+ if (origin == drflac_seek_origin_start) {
+ if (offset <= 0x7FFFFFFF) {
+ if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos = offset;
+
+ return DRFLAC_TRUE;
+ } else {
+ if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos = offset;
+
+ return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
+ }
+ } else {
+ while (offset > 0x7FFFFFFF) {
+ if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos += 0x7FFFFFFF;
+ offset -= 0x7FFFFFFF;
+ }
+
+ if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { // <-- Safe cast thanks to the loop above.
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos += offset;
+
+ return DRFLAC_TRUE;
+ }
+}
+
+static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod)
+{
+ drflac_ogg_page_header header;
+ for (;;) {
+ drflac_uint32 crc32 = 0;
+ drflac_uint32 bytesRead;
+ if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos += bytesRead;
+
+ drflac_uint32 pageBodySize = drflac_ogg__get_page_body_size(&header);
+ if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) {
+ continue; // Invalid page size. Assume it's corrupted and just move to the next page.
+ }
+
+ if (header.serialNumber != oggbs->serialNumber) {
+ // It's not a FLAC page. Skip it.
+ if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ continue;
+ }
+
+
+ // We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page.
+ if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->pageDataSize = pageBodySize;
+
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint32 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
+ if (actualCRC32 != header.checksum) {
+ if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) {
+ continue; // CRC mismatch. Skip this page.
+ } else {
+ // Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we
+ // go to the next valid page to ensure we're in a good state, but return false to let the caller know that the
+ // seek did not fully complete.
+ drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch);
+ return DRFLAC_FALSE;
+ }
+ }
+#else
+ (void)recoveryMethod; // <-- Silence a warning.
+#endif
+
+ oggbs->currentPageHeader = header;
+ oggbs->bytesRemainingInPage = pageBodySize;
+ return DRFLAC_TRUE;
+ }
+}
+
+// Function below is unused at the moment, but I might be re-adding it later.
+#if 0
+static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg)
+{
+ drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
+ drflac_uint8 iSeg = 0;
+ drflac_uint32 iByte = 0;
+ while (iByte < bytesConsumedInPage) {
+ drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
+ if (iByte + segmentSize > bytesConsumedInPage) {
+ break;
+ } else {
+ iSeg += 1;
+ iByte += segmentSize;
+ }
+ }
+
+ *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte);
+ return iSeg;
+}
+
+static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
+{
+ // The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page.
+ for (;;) {
+ drflac_bool32 atEndOfPage = DRFLAC_FALSE;
+
+ drflac_uint8 bytesRemainingInSeg;
+ drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
+
+ drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
+ for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
+ drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
+ if (segmentSize < 255) {
+ if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
+ atEndOfPage = DRFLAC_TRUE;
+ }
+
+ break;
+ }
+
+ bytesToEndOfPacketOrPage += segmentSize;
+ }
+
+ // At this point we will have found either the packet or the end of the page. If were at the end of the page we'll
+ // want to load the next page and keep searching for the end of the packet.
+ drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
+ oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
+
+ if (atEndOfPage) {
+ // We're potentially at the next packet, but we need to check the next page first to be sure because the packet may
+ // straddle pages.
+ if (!drflac_oggbs__goto_next_page(oggbs)) {
+ return DRFLAC_FALSE;
+ }
+
+ // If it's a fresh packet it most likely means we're at the next packet.
+ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
+ return DRFLAC_TRUE;
+ }
+ } else {
+ // We're at the next packet.
+ return DRFLAC_TRUE;
+ }
+ }
+}
+
+static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
+{
+ // The bitstream should be sitting on the first byte just after the header of the frame.
+
+ // What we're actually doing here is seeking to the start of the next packet.
+ return drflac_oggbs__seek_to_next_packet(oggbs);
+}
+#endif
+
+static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
+ drflac_assert(oggbs != NULL);
+
+ drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut;
+
+ // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one.
+ size_t bytesRead = 0;
+ while (bytesRead < bytesToRead) {
+ size_t bytesRemainingToRead = bytesToRead - bytesRead;
+
+ if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
+ drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
+ bytesRead += bytesRemainingToRead;
+ oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead;
+ break;
+ }
+
+ // If we get here it means some of the requested data is contained in the next pages.
+ if (oggbs->bytesRemainingInPage > 0) {
+ drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
+ bytesRead += oggbs->bytesRemainingInPage;
+ pRunningBufferOut += oggbs->bytesRemainingInPage;
+ oggbs->bytesRemainingInPage = 0;
+ }
+
+ drflac_assert(bytesRemainingToRead > 0);
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
+ break; // Failed to go to the next page. Might have simply hit the end of the stream.
+ }
+ }
+
+ return bytesRead;
+}
+
+static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
+ drflac_assert(oggbs != NULL);
+ drflac_assert(offset >= 0); // <-- Never seek backwards.
+
+ // Seeking is always forward which makes things a lot simpler.
+ if (origin == drflac_seek_origin_start) {
+ if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
+ return DRFLAC_FALSE;
+ }
+
+ return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
+ }
+
+
+ drflac_assert(origin == drflac_seek_origin_current);
+
+ int bytesSeeked = 0;
+ while (bytesSeeked < offset) {
+ int bytesRemainingToSeek = offset - bytesSeeked;
+ drflac_assert(bytesRemainingToSeek >= 0);
+
+ if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
+ bytesSeeked += bytesRemainingToSeek;
+ oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
+ break;
+ }
+
+ // If we get here it means some of the requested data is contained in the next pages.
+ if (oggbs->bytesRemainingInPage > 0) {
+ bytesSeeked += (int)oggbs->bytesRemainingInPage;
+ oggbs->bytesRemainingInPage = 0;
+ }
+
+ drflac_assert(bytesRemainingToSeek > 0);
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
+ // Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch.
+ return DRFLAC_FALSE;
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+
+ drflac_uint64 originalBytePos = oggbs->currentBytePos; // For recovery.
+
+ // First seek to the first frame.
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->bytesRemainingInPage = 0;
+
+ drflac_uint64 runningGranulePosition = 0;
+ drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier.
+ for (;;) {
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
+ drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
+ return DRFLAC_FALSE; // Never did find that sample...
+ }
+
+ runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
+ if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) {
+ break; // The sample is somewhere in the previous page.
+ }
+
+
+ // At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we
+ // disregard any pages that do not begin a fresh packet.
+ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { // <-- Is it a fresh page?
+ if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
+ drflac_uint8 firstBytesInPage[2];
+ firstBytesInPage[0] = oggbs->pageData[0];
+ firstBytesInPage[1] = oggbs->pageData[1];
+
+ if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code?
+ runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels;
+ }
+
+ continue;
+ }
+ }
+ }
+
+
+ // We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the
+ // start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of
+ // a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until
+ // we find the one containing the target sample.
+ if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
+ return DRFLAC_FALSE;
+ }
+
+
+ // At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep
+ // looping over these frames until we find the one containing the sample we're after.
+ drflac_uint64 runningSampleCount = runningGranulePosition;
+ for (;;) {
+ // There are two ways to find the sample and seek past irrelevant frames:
+ // 1) Use the native FLAC decoder.
+ // 2) Use Ogg's framing system.
+ //
+ // Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to
+ // do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code
+ // duplication for the decoding of frame headers.
+ //
+ // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg
+ // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the
+ // standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks
+ // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read
+ // using the native FLAC decoding APIs, such as drflac__read_next_flac_frame_header(), need to be re-implemented so as to
+ // avoid the use of the drflac_bs object.
+ //
+ // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons:
+ // 1) Seeking is already partially accelerated using Ogg's paging system in the code block above.
+ // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon.
+ // 3) Simplicity.
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint64 firstSampleInFrame = 0;
+ drflac_uint64 lastSampleInFrame = 0;
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
+
+ drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
+ // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend
+ // it never existed and keep iterating.
+ drflac_result result = drflac__decode_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
+ drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535.
+ if (samplesToDecode == 0) {
+ return DRFLAC_TRUE;
+ }
+ return drflac__seek_forward_by_samples(pFlac, samplesToDecode) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail).
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ continue; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ // frame never existed and leave the running sample count untouched.
+ drflac_result result = drflac__seek_to_next_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningSampleCount += sampleCountInThisFrame;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ continue; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ }
+ }
+}
+
+
+drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
+{
+ // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern.
+ (void)relaxed;
+
+ pInit->container = drflac_container_ogg;
+ pInit->oggFirstBytePos = 0;
+
+ // We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the
+ // stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if
+ // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed.
+ drflac_ogg_page_header header;
+
+ drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
+ drflac_uint32 bytesRead = 0;
+ if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
+ return DRFLAC_FALSE;
+ }
+ pInit->runningFilePos += bytesRead;
+
+ for (;;) {
+ // Break if we're past the beginning of stream page.
+ if ((header.headerType & 0x02) == 0) {
+ return DRFLAC_FALSE;
+ }
+
+
+ // Check if it's a FLAC header.
+ int pageBodySize = drflac_ogg__get_page_body_size(&header);
+ if (pageBodySize == 51) { // 51 = the lacing value of the FLAC header packet.
+ // It could be a FLAC page...
+ drflac_uint32 bytesRemainingInPage = pageBodySize;
+
+ drflac_uint8 packetType;
+ if (onRead(pUserData, &packetType, 1) != 1) {
+ return DRFLAC_FALSE;
+ }
+
+ bytesRemainingInPage -= 1;
+ if (packetType == 0x7F) {
+ // Increasingly more likely to be a FLAC page...
+ drflac_uint8 sig[4];
+ if (onRead(pUserData, sig, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ bytesRemainingInPage -= 4;
+ if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
+ // Almost certainly a FLAC page...
+ drflac_uint8 mappingVersion[2];
+ if (onRead(pUserData, mappingVersion, 2) != 2) {
+ return DRFLAC_FALSE;
+ }
+
+ if (mappingVersion[0] != 1) {
+ return DRFLAC_FALSE; // Only supporting version 1.x of the Ogg mapping.
+ }
+
+ // The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to
+ // be handling it in a generic way based on the serial number and packet types.
+ if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Expecting the native FLAC signature "fLaC".
+ if (onRead(pUserData, sig, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
+ // The remaining data in the page should be the STREAMINFO block.
+ drflac_uint8 isLastBlock;
+ drflac_uint8 blockType;
+ drflac_uint32 blockSize;
+ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
+ return DRFLAC_FALSE; // Invalid block type. First block must be the STREAMINFO block.
+ }
+
+ drflac_streaminfo streaminfo;
+ if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
+ // Success!
+ pInit->hasStreamInfoBlock = DRFLAC_TRUE;
+ pInit->sampleRate = streaminfo.sampleRate;
+ pInit->channels = streaminfo.channels;
+ pInit->bitsPerSample = streaminfo.bitsPerSample;
+ pInit->totalSampleCount = streaminfo.totalSampleCount;
+ pInit->maxBlockSize = streaminfo.maxBlockSize;
+ pInit->hasMetadataBlocks = !isLastBlock;
+
+ if (onMeta) {
+ drflac_metadata metadata;
+ metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+ metadata.data.streaminfo = streaminfo;
+ onMeta(pUserDataMD, &metadata);
+ }
+
+ pInit->runningFilePos += pageBodySize;
+ pInit->oggFirstBytePos = pInit->runningFilePos - 79; // Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page.
+ pInit->oggSerial = header.serialNumber;
+ pInit->oggBosHeader = header;
+ break;
+ } else {
+ // Failed to read STREAMINFO block. Aww, so close...
+ return DRFLAC_FALSE;
+ }
+ } else {
+ // Invalid file.
+ return DRFLAC_FALSE;
+ }
+ } else {
+ // Not a FLAC header. Skip it.
+ if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // Not a FLAC header. Seek past the entire page and move on to the next.
+ if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ pInit->runningFilePos += pageBodySize;
+
+
+ // Read the header of the next page.
+ if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
+ return DRFLAC_FALSE;
+ }
+ pInit->runningFilePos += bytesRead;
+ }
+
+
+ // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next
+ // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the
+ // Ogg bistream object.
+ pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block.
+ return DRFLAC_TRUE;
+}
+#endif
+
+drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
+{
+ if (pInit == NULL || onRead == NULL || onSeek == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_zero_memory(pInit, sizeof(*pInit));
+ pInit->onRead = onRead;
+ pInit->onSeek = onSeek;
+ pInit->onMeta = onMeta;
+ pInit->container = container;
+ pInit->pUserData = pUserData;
+ pInit->pUserDataMD = pUserDataMD;
+
+ pInit->bs.onRead = onRead;
+ pInit->bs.onSeek = onSeek;
+ pInit->bs.pUserData = pUserData;
+ drflac__reset_cache(&pInit->bs);
+
+
+ // If the container is explicitly defined then we can try opening in relaxed mode.
+ drflac_bool32 relaxed = container != drflac_container_unknown;
+
+ drflac_uint8 id[4];
+
+ // Skip over any ID3 tags.
+ for (;;) {
+ if (onRead(pUserData, id, 4) != 4) {
+ return DRFLAC_FALSE; // Ran out of data.
+ }
+ pInit->runningFilePos += 4;
+
+ if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
+ drflac_uint8 header[6];
+ if (onRead(pUserData, header, 6) != 6) {
+ return DRFLAC_FALSE; // Ran out of data.
+ }
+ pInit->runningFilePos += 6;
+
+ drflac_uint8 flags = header[1];
+ drflac_uint32 headerSize;
+ drflac_copy_memory(&headerSize, header+2, 4);
+ headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize));
+ if (flags & 0x10) {
+ headerSize += 10;
+ }
+
+ if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE; // Failed to seek past the tag.
+ }
+ pInit->runningFilePos += headerSize;
+ } else {
+ break;
+ }
+ }
+
+ if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
+ return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
+ }
+#ifndef DR_FLAC_NO_OGG
+ if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
+ return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
+ }
+#endif
+
+ // If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable.
+ if (relaxed) {
+ if (container == drflac_container_native) {
+ return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
+ }
+#ifndef DR_FLAC_NO_OGG
+ if (container == drflac_container_ogg) {
+ return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
+ }
+#endif
+ }
+
+ // Unsupported container.
+ return DRFLAC_FALSE;
+}
+
+void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit)
+{
+ drflac_assert(pFlac != NULL);
+ drflac_assert(pInit != NULL);
+
+ drflac_zero_memory(pFlac, sizeof(*pFlac));
+ pFlac->bs = pInit->bs;
+ pFlac->onMeta = pInit->onMeta;
+ pFlac->pUserDataMD = pInit->pUserDataMD;
+ pFlac->maxBlockSize = pInit->maxBlockSize;
+ pFlac->sampleRate = pInit->sampleRate;
+ pFlac->channels = (drflac_uint8)pInit->channels;
+ pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
+ pFlac->totalSampleCount = pInit->totalSampleCount;
+ pFlac->totalPCMFrameCount = pInit->totalSampleCount / pFlac->channels;
+ pFlac->container = pInit->container;
+}
+
+drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
+{
+#ifndef DRFLAC_NO_CPUID
+ // CPU support first.
+ drflac__init_cpu_caps();
+#endif
+
+ drflac_init_info init;
+ if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
+ return NULL;
+ }
+
+ // The size of the allocation for the drflac object needs to be large enough to fit the following:
+ // 1) The main members of the drflac structure
+ // 2) A block of memory large enough to store the decoded samples of the largest frame in the stream
+ // 3) If the container is Ogg, a drflac_oggbs object
+ //
+ // The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration
+ // the different SIMD instruction sets.
+ drflac_uint32 allocationSize = sizeof(drflac);
+
+ // The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector
+ // we are supporting.
+ drflac_uint32 wholeSIMDVectorCountPerChannel;
+ if (((init.maxBlockSize+DRFLAC_LEADING_SAMPLES) % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
+ wholeSIMDVectorCountPerChannel = ((init.maxBlockSize+DRFLAC_LEADING_SAMPLES) / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
+ } else {
+ wholeSIMDVectorCountPerChannel = ((init.maxBlockSize+DRFLAC_LEADING_SAMPLES) / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
+ }
+
+ drflac_uint32 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
+
+ allocationSize += decodedSamplesAllocationSize;
+ allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; // Allocate extra bytes to ensure we have enough for alignment.
+
+#ifndef DR_FLAC_NO_OGG
+ // There's additional data required for Ogg streams.
+ drflac_uint32 oggbsAllocationSize = 0;
+ if (init.container == drflac_container_ogg) {
+ oggbsAllocationSize = sizeof(drflac_oggbs);
+ allocationSize += oggbsAllocationSize;
+ }
+
+ drflac_oggbs oggbs;
+ drflac_zero_memory(&oggbs, sizeof(oggbs));
+ if (init.container == drflac_container_ogg) {
+ oggbs.onRead = onRead;
+ oggbs.onSeek = onSeek;
+ oggbs.pUserData = pUserData;
+ oggbs.currentBytePos = init.oggFirstBytePos;
+ oggbs.firstBytePos = init.oggFirstBytePos;
+ oggbs.serialNumber = init.oggSerial;
+ oggbs.bosPageHeader = init.oggBosHeader;
+ oggbs.bytesRemainingInPage = 0;
+ }
+#endif
+
+ // This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to
+ // consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading
+ // and decoding the metadata.
+ drflac_uint64 firstFramePos = 42; // <-- We know we are at byte 42 at this point.
+ drflac_uint64 seektablePos = 0;
+ drflac_uint32 seektableSize = 0;
+ if (init.hasMetadataBlocks) {
+ drflac_read_proc onReadOverride = onRead;
+ drflac_seek_proc onSeekOverride = onSeek;
+ void* pUserDataOverride = pUserData;
+
+#ifndef DR_FLAC_NO_OGG
+ if (init.container == drflac_container_ogg) {
+ onReadOverride = drflac__on_read_ogg;
+ onSeekOverride = drflac__on_seek_ogg;
+ pUserDataOverride = (void*)&oggbs;
+ }
+#endif
+
+ if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize)) {
+ return NULL;
+ }
+
+ allocationSize += seektableSize;
+ }
+
+
+ drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize);
+ drflac__init_from_info(pFlac, &init);
+ pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE);
+
+#ifndef DR_FLAC_NO_OGG
+ if (init.container == drflac_container_ogg) {
+ drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize);
+ *pInternalOggbs = oggbs;
+
+ // The Ogg bistream needs to be layered on top of the original bitstream.
+ pFlac->bs.onRead = drflac__on_read_ogg;
+ pFlac->bs.onSeek = drflac__on_seek_ogg;
+ pFlac->bs.pUserData = (void*)pInternalOggbs;
+ pFlac->_oggbs = (void*)pInternalOggbs;
+ }
+#endif
+
+ pFlac->firstFramePos = firstFramePos;
+
+ // NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now.
+#ifndef DR_FLAC_NO_OGG
+ if (init.container == drflac_container_ogg)
+ {
+ pFlac->pSeekpoints = NULL;
+ pFlac->seekpointCount = 0;
+ }
+ else
+#endif
+ {
+ // If we have a seektable we need to load it now, making sure we move back to where we were previously.
+ if (seektablePos != 0) {
+ pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints);
+ pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
+
+ // Seek to the seektable, then just read directly into our seektable buffer.
+ if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
+ if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) {
+ // Endian swap.
+ for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
+ pFlac->pSeekpoints[iSeekpoint].firstSample = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstSample);
+ pFlac->pSeekpoints[iSeekpoint].frameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].frameOffset);
+ pFlac->pSeekpoints[iSeekpoint].sampleCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].sampleCount);
+ }
+ } else {
+ // Failed to read the seektable. Pretend we don't have one.
+ pFlac->pSeekpoints = NULL;
+ pFlac->seekpointCount = 0;
+ }
+
+ // We need to seek back to where we were. If this fails it's a critical error.
+ if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFramePos, drflac_seek_origin_start)) {
+ DRFLAC_FREE(pFlac);
+ return NULL;
+ }
+ } else {
+ // Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one.
+ pFlac->pSeekpoints = NULL;
+ pFlac->seekpointCount = 0;
+ }
+ }
+ }
+
+
+
+ // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode
+ // the first frame.
+ if (!init.hasStreamInfoBlock) {
+ pFlac->currentFrame.header = init.firstFrameHeader;
+ do
+ {
+ drflac_result result = drflac__decode_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ break;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ DRFLAC_FREE(pFlac);
+ return NULL;
+ }
+ continue;
+ } else {
+ DRFLAC_FREE(pFlac);
+ return NULL;
+ }
+ }
+ } while (1);
+ }
+
+ return pFlac;
+}
+
+
+
+#ifndef DR_FLAC_NO_STDIO
+#include
+
+static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
+}
+
+static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ drflac_assert(offset >= 0); // <-- Never seek backwards.
+
+ return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+static FILE* drflac__fopen(const char* filename)
+{
+ FILE* pFile;
+#ifdef _MSC_VER
+ if (fopen_s(&pFile, filename, "rb") != 0) {
+ return NULL;
+ }
+#else
+ pFile = fopen(filename, "rb");
+ if (pFile == NULL) {
+ return NULL;
+ }
+#endif
+
+ return pFile;
+}
+
+
+drflac* drflac_open_file(const char* filename)
+{
+ FILE* file = drflac__fopen(filename);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ drflac* pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)file);
+ if (pFlac == NULL) {
+ fclose(file);
+ return NULL;
+ }
+
+ return pFlac;
+}
+
+drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData)
+{
+ FILE* file = drflac__fopen(filename);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)file, pUserData);
+ if (pFlac == NULL) {
+ fclose(file);
+ return pFlac;
+ }
+
+ return pFlac;
+}
+#endif //DR_FLAC_NO_STDIO
+
+static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+ drflac_assert(memoryStream != NULL);
+ drflac_assert(memoryStream->dataSize >= memoryStream->currentReadPos);
+
+ size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
+ if (bytesToRead > bytesRemaining) {
+ bytesToRead = bytesRemaining;
+ }
+
+ if (bytesToRead > 0) {
+ drflac_copy_memory(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
+ memoryStream->currentReadPos += bytesToRead;
+ }
+
+ return bytesToRead;
+}
+
+static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+ drflac_assert(memoryStream != NULL);
+ drflac_assert(offset >= 0); // <-- Never seek backwards.
+
+ if (offset > (drflac_int64)memoryStream->dataSize) {
+ return DRFLAC_FALSE;
+ }
+
+ if (origin == drflac_seek_origin_current) {
+ if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
+ memoryStream->currentReadPos += offset;
+ } else {
+ return DRFLAC_FALSE; // Trying to seek too far forward.
+ }
+ } else {
+ if ((drflac_uint32)offset <= memoryStream->dataSize) {
+ memoryStream->currentReadPos = offset;
+ } else {
+ return DRFLAC_FALSE; // Trying to seek too far forward.
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+drflac* drflac_open_memory(const void* data, size_t dataSize)
+{
+ drflac__memory_stream memoryStream;
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+ drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ pFlac->memoryStream = memoryStream;
+
+ // This is an awful hack...
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ oggbs->pUserData = &pFlac->memoryStream;
+ }
+ else
+#endif
+ {
+ pFlac->bs.pUserData = &pFlac->memoryStream;
+ }
+
+ return pFlac;
+}
+
+drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData)
+{
+ drflac__memory_stream memoryStream;
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+ drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ pFlac->memoryStream = memoryStream;
+
+ // This is an awful hack...
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ oggbs->pUserData = &pFlac->memoryStream;
+ }
+ else
+#endif
+ {
+ pFlac->bs.pUserData = &pFlac->memoryStream;
+ }
+
+ return pFlac;
+}
+
+
+
+drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData);
+}
+drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData);
+}
+
+drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData);
+}
+drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData);
+}
+
+void drflac_close(drflac* pFlac)
+{
+ if (pFlac == NULL) {
+ return;
+ }
+
+#ifndef DR_FLAC_NO_STDIO
+ // If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file()
+ // was used by looking at the callbacks.
+ if (pFlac->bs.onRead == drflac__on_read_stdio) {
+ fclose((FILE*)pFlac->bs.pUserData);
+ }
+
+#ifndef DR_FLAC_NO_OGG
+ // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained.
+ if (pFlac->container == drflac_container_ogg) {
+ drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg);
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ if (oggbs->onRead == drflac__on_read_stdio) {
+ fclose((FILE*)oggbs->pUserData);
+ }
+ }
+#endif
+#endif
+
+ DRFLAC_FREE(pFlac);
+}
+
+drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut)
+{
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+
+ // We should never be calling this when the number of samples to read is >= the sample count.
+ drflac_assert(samplesToRead < channelCount);
+ drflac_assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining);
+
+
+ drflac_uint64 samplesRead = 0;
+ while (samplesToRead > 0) {
+ drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount;
+ drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining;
+ drflac_uint64 channelIndex = samplesReadFromFrameSoFar % channelCount;
+
+ drflac_uint64 nextSampleInFrame = samplesReadFromFrameSoFar / channelCount;
+
+ int decodedSample = 0;
+ switch (pFlac->currentFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ if (channelIndex == 0) {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ } else {
+ int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample;
+ decodedSample = left - side;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ if (channelIndex == 0) {
+ int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample;
+ decodedSample = side + right;
+ } else {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ int mid;
+ int side;
+ if (channelIndex == 0) {
+ mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample;
+
+ mid = (((unsigned int)mid) << 1) | (side & 0x01);
+ decodedSample = (mid + side) >> 1;
+ } else {
+ mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample;
+ side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+
+ mid = (((unsigned int)mid) << 1) | (side & 0x01);
+ decodedSample = (mid - side) >> 1;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ } break;
+ }
+
+
+ decodedSample <<= (32 - pFlac->bitsPerSample);
+
+ if (bufferOut) {
+ *bufferOut++ = decodedSample;
+ }
+
+ samplesRead += 1;
+ pFlac->currentFrame.samplesRemaining -= 1;
+ samplesToRead -= 1;
+ }
+
+ return samplesRead;
+}
+
+drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut)
+{
+ // Note that is allowed to be null, in which case this will act like a seek.
+ if (pFlac == NULL || samplesToRead == 0) {
+ return 0;
+ }
+
+ if (bufferOut == NULL) {
+ return drflac__seek_forward_by_samples(pFlac, samplesToRead);
+ }
+
+
+ drflac_uint64 samplesRead = 0;
+ while (samplesToRead > 0) {
+ // If we've run out of samples in this frame, go to the next.
+ if (pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; // Couldn't read the next frame, so just break from the loop and return.
+ }
+ } else {
+ // Here is where we grab the samples and interleave them.
+
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount;
+ drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining;
+
+ drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount;
+ if (misalignedSampleCount > 0) {
+ drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut);
+ samplesRead += misalignedSamplesRead;
+ samplesReadFromFrameSoFar += misalignedSamplesRead;
+ bufferOut += misalignedSamplesRead;
+ samplesToRead -= misalignedSamplesRead;
+ pFlac->currentSample += misalignedSamplesRead;
+ }
+
+
+ drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount;
+ if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) {
+ alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount;
+ }
+
+ drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount;
+ unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+
+ switch (pFlac->currentFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int left = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int side = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ int right = left - side;
+
+ bufferOut[i*2+0] = left;
+ bufferOut[i*2+1] = right;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int side = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int right = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ int left = right + side;
+
+ bufferOut[i*2+0] = left;
+ bufferOut[i*2+1] = right;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int mid = pDecodedSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pDecodedSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample);
+ bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample);
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo
+ {
+ // Stereo optimized inner loop unroll.
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ }
+ }
+ else
+ {
+ // Generic interleaving.
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ for (unsigned int j = 0; j < channelCount; ++j) {
+ bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample);
+ }
+ }
+ }
+ } break;
+ }
+
+ drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount;
+ samplesRead += alignedSamplesRead;
+ samplesReadFromFrameSoFar += alignedSamplesRead;
+ bufferOut += alignedSamplesRead;
+ samplesToRead -= alignedSamplesRead;
+ pFlac->currentSample += alignedSamplesRead;
+ pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead;
+
+
+ // At this point we may still have some excess samples left to read.
+ if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) {
+ drflac_uint64 excessSamplesRead = 0;
+ if (samplesToRead < pFlac->currentFrame.samplesRemaining) {
+ excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut);
+ } else {
+ excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut);
+ }
+
+ samplesRead += excessSamplesRead;
+ samplesReadFromFrameSoFar += excessSamplesRead;
+ bufferOut += excessSamplesRead;
+ samplesToRead -= excessSamplesRead;
+ pFlac->currentSample += excessSamplesRead;
+ }
+ }
+ }
+
+ return samplesRead;
+}
+
+drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
+{
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(push)
+ #pragma warning(disable:4996) // was declared deprecated
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ return drflac_read_s32(pFlac, framesToRead*pFlac->channels, pBufferOut) / pFlac->channels;
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(pop)
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic pop
+#endif
+}
+
+
+drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut)
+{
+ // This reads samples in 2 passes and can probably be optimized.
+ drflac_uint64 totalSamplesRead = 0;
+
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(push)
+ #pragma warning(disable:4996) // was declared deprecated
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+ while (samplesToRead > 0) {
+ drflac_int32 samples32[4096];
+ drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32);
+ if (samplesJustRead == 0) {
+ break; // Reached the end.
+ }
+
+ // s32 -> s16
+ for (drflac_uint64 i = 0; i < samplesJustRead; ++i) {
+ pBufferOut[i] = (drflac_int16)(samples32[i] >> 16);
+ }
+
+ totalSamplesRead += samplesJustRead;
+ samplesToRead -= samplesJustRead;
+ pBufferOut += samplesJustRead;
+ }
+
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(pop)
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic pop
+#endif
+
+ return totalSamplesRead;
+}
+
+drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
+{
+ // This reads samples in 2 passes and can probably be optimized.
+ drflac_uint64 totalPCMFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drflac_int32 samples32[4096];
+ drflac_uint64 framesJustRead = drflac_read_pcm_frames_s32(pFlac, (framesToRead > 4096/pFlac->channels) ? 4096/pFlac->channels : framesToRead, samples32);
+ if (framesJustRead == 0) {
+ break; // Reached the end.
+ }
+
+ // s32 -> s16
+ for (drflac_uint64 iFrame = 0; iFrame < framesJustRead; ++iFrame) {
+ for (drflac_uint32 iChannel = 0; iChannel < pFlac->channels; ++iChannel) {
+ drflac_uint64 iSample = iFrame*pFlac->channels + iChannel;
+ pBufferOut[iSample] = (drflac_int16)(samples32[iSample] >> 16);
+ }
+ }
+
+ totalPCMFramesRead += framesJustRead;
+ framesToRead -= framesJustRead;
+ pBufferOut += framesJustRead * pFlac->channels;
+ }
+
+ return totalPCMFramesRead;
+}
+
+
+drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut)
+{
+ // This reads samples in 2 passes and can probably be optimized.
+ drflac_uint64 totalSamplesRead = 0;
+
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(push)
+ #pragma warning(disable:4996) // was declared deprecated
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+ while (samplesToRead > 0) {
+ drflac_int32 samples32[4096];
+ drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32);
+ if (samplesJustRead == 0) {
+ break; // Reached the end.
+ }
+
+ // s32 -> f32
+ for (drflac_uint64 i = 0; i < samplesJustRead; ++i) {
+ pBufferOut[i] = (float)(samples32[i] / 2147483648.0);
+ }
+
+ totalSamplesRead += samplesJustRead;
+ samplesToRead -= samplesJustRead;
+ pBufferOut += samplesJustRead;
+ }
+
+#if defined(_MSC_VER) && !defined(__clang__)
+ #pragma warning(pop)
+#elif defined(__GNUC__) || defined(__clang__)
+ #pragma GCC diagnostic pop
+#endif
+
+ return totalSamplesRead;
+}
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ int left = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int side = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ int right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)(right / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ drflac_int32 left0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 left1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 left2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 left3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 right0 = left0 - side0;
+ drflac_int32 right1 = left1 - side1;
+ drflac_int32 right2 = left2 - side2;
+ drflac_int32 right3 = left3 - side3;
+
+ pOutputSamples[i*8+0] = left0 * factor;
+ pOutputSamples[i*8+1] = right0 * factor;
+ pOutputSamples[i*8+2] = left1 * factor;
+ pOutputSamples[i*8+3] = right1 * factor;
+ pOutputSamples[i*8+4] = left2 * factor;
+ pOutputSamples[i*8+5] = right2 * factor;
+ pOutputSamples[i*8+6] = left3 * factor;
+ pOutputSamples[i*8+7] = right3 * factor;
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ int left = pInputSamples0[i] << shift0;
+ int side = pInputSamples1[i] << shift1;
+ int right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left * factor);
+ pOutputSamples[i*2+1] = (float)(right * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_assert(pFlac->bitsPerSample <= 24);
+
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ __m128 factor = _mm_set1_ps(1.0f / 8388608.0f);
+ int shift0 = (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample) - 8;
+ int shift1 = (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i left = _mm_slli_epi32(inputSample0, shift0);
+ __m128i side = _mm_slli_epi32(inputSample1, shift1);
+ __m128i right = _mm_sub_epi32(left, side);
+ __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
+ __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
+
+ pOutputSamples[i*8+0] = ((float*)&leftf)[0];
+ pOutputSamples[i*8+1] = ((float*)&rightf)[0];
+ pOutputSamples[i*8+2] = ((float*)&leftf)[1];
+ pOutputSamples[i*8+3] = ((float*)&rightf)[1];
+ pOutputSamples[i*8+4] = ((float*)&leftf)[2];
+ pOutputSamples[i*8+5] = ((float*)&rightf)[2];
+ pOutputSamples[i*8+6] = ((float*)&leftf)[3];
+ pOutputSamples[i*8+7] = ((float*)&rightf)[3];
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ int left = pInputSamples0[i] << shift0;
+ int side = pInputSamples1[i] << shift1;
+ int right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left / 8388608.0f);
+ pOutputSamples[i*2+1] = (float)(right / 8388608.0f);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ // Scalar fallback.
+#if 0
+ drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ int side = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int right = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ int left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)(right / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ drflac_int32 side0 = pInputSamples0[i*4+0] << shift1;
+ drflac_int32 side1 = pInputSamples0[i*4+1] << shift1;
+ drflac_int32 side2 = pInputSamples0[i*4+2] << shift1;
+ drflac_int32 side3 = pInputSamples0[i*4+3] << shift1;
+
+ drflac_int32 right0 = pInputSamples1[i*4+0] << shift0;
+ drflac_int32 right1 = pInputSamples1[i*4+1] << shift0;
+ drflac_int32 right2 = pInputSamples1[i*4+2] << shift0;
+ drflac_int32 right3 = pInputSamples1[i*4+3] << shift0;
+
+ drflac_int32 left0 = right0 + side0;
+ drflac_int32 left1 = right1 + side1;
+ drflac_int32 left2 = right2 + side2;
+ drflac_int32 left3 = right3 + side3;
+
+ pOutputSamples[i*8+0] = left0 * factor;
+ pOutputSamples[i*8+1] = right0 * factor;
+ pOutputSamples[i*8+2] = left1 * factor;
+ pOutputSamples[i*8+3] = right1 * factor;
+ pOutputSamples[i*8+4] = left2 * factor;
+ pOutputSamples[i*8+5] = right2 * factor;
+ pOutputSamples[i*8+6] = left3 * factor;
+ pOutputSamples[i*8+7] = right3 * factor;
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ int side = pInputSamples0[i] << shift0;
+ int right = pInputSamples1[i] << shift1;
+ int left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left * factor);
+ pOutputSamples[i*2+1] = (float)(right * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_assert(pFlac->bitsPerSample <= 24);
+
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ __m128 factor = _mm_set1_ps(1.0f / 8388608.0f);
+ int shift0 = (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample) - 8;
+ int shift1 = (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i side = _mm_slli_epi32(inputSample0, shift0);
+ __m128i right = _mm_slli_epi32(inputSample1, shift1);
+ __m128i left = _mm_add_epi32(right, side);
+ __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
+ __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
+
+ pOutputSamples[i*8+0] = ((float*)&leftf)[0];
+ pOutputSamples[i*8+1] = ((float*)&rightf)[0];
+ pOutputSamples[i*8+2] = ((float*)&leftf)[1];
+ pOutputSamples[i*8+3] = ((float*)&rightf)[1];
+ pOutputSamples[i*8+4] = ((float*)&leftf)[2];
+ pOutputSamples[i*8+5] = ((float*)&rightf)[2];
+ pOutputSamples[i*8+6] = ((float*)&leftf)[3];
+ pOutputSamples[i*8+7] = ((float*)&rightf)[3];
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ int side = pInputSamples0[i] << shift0;
+ int right = pInputSamples1[i] << shift1;
+ int left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left / 8388608.0f);
+ pOutputSamples[i*2+1] = (float)(right / 8388608.0f);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ // Scalar fallback.
+#if 0
+ drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ int shift = unusedBitsPerSample;
+ if (shift > 0) {
+ shift -= 1;
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ int mid0 = pInputSamples0[i*4+0] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid1 = pInputSamples0[i*4+1] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid2 = pInputSamples0[i*4+2] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid3 = pInputSamples0[i*4+3] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+
+ int side0 = pInputSamples1[i*4+0] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side1 = pInputSamples1[i*4+1] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side2 = pInputSamples1[i*4+2] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side3 = pInputSamples1[i*4+3] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ int temp0L = ((mid0 + side0) << shift);
+ int temp1L = ((mid1 + side1) << shift);
+ int temp2L = ((mid2 + side2) << shift);
+ int temp3L = ((mid3 + side3) << shift);
+
+ int temp0R = ((mid0 - side0) << shift);
+ int temp1R = ((mid1 - side1) << shift);
+ int temp2R = ((mid2 - side2) << shift);
+ int temp3R = ((mid3 - side3) << shift);
+
+ pOutputSamples[i*8+0] = (float)(temp0L * factor);
+ pOutputSamples[i*8+1] = (float)(temp0R * factor);
+ pOutputSamples[i*8+2] = (float)(temp1L * factor);
+ pOutputSamples[i*8+3] = (float)(temp1R * factor);
+ pOutputSamples[i*8+4] = (float)(temp2L * factor);
+ pOutputSamples[i*8+5] = (float)(temp2R * factor);
+ pOutputSamples[i*8+6] = (float)(temp3L * factor);
+ pOutputSamples[i*8+7] = (float)(temp3R * factor);
+ }
+ } else {
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ int mid0 = pInputSamples0[i*4+0] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid1 = pInputSamples0[i*4+1] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid2 = pInputSamples0[i*4+2] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int mid3 = pInputSamples0[i*4+3] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+
+ int side0 = pInputSamples1[i*4+0] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side1 = pInputSamples1[i*4+1] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side2 = pInputSamples1[i*4+2] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+ int side3 = pInputSamples1[i*4+3] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ int temp0L = ((mid0 + side0) >> 1);
+ int temp1L = ((mid1 + side1) >> 1);
+ int temp2L = ((mid2 + side2) >> 1);
+ int temp3L = ((mid3 + side3) >> 1);
+
+ int temp0R = ((mid0 - side0) >> 1);
+ int temp1R = ((mid1 - side1) >> 1);
+ int temp2R = ((mid2 - side2) >> 1);
+ int temp3R = ((mid3 - side3) >> 1);
+
+ pOutputSamples[i*8+0] = (float)(temp0L * factor);
+ pOutputSamples[i*8+1] = (float)(temp0R * factor);
+ pOutputSamples[i*8+2] = (float)(temp1L * factor);
+ pOutputSamples[i*8+3] = (float)(temp1R * factor);
+ pOutputSamples[i*8+4] = (float)(temp2L * factor);
+ pOutputSamples[i*8+5] = (float)(temp2R * factor);
+ pOutputSamples[i*8+6] = (float)(temp3L * factor);
+ pOutputSamples[i*8+7] = (float)(temp3R * factor);
+ }
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << unusedBitsPerSample) * factor);
+ pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << unusedBitsPerSample) * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_assert(pFlac->bitsPerSample <= 24);
+
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1.0f / 8388608.0f;
+ __m128 factor128 = _mm_set1_ps(1.0f / 8388608.0f);
+
+ int shift = unusedBitsPerSample - 8;
+ if (shift == 0) {
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i mid = _mm_slli_epi32(inputSample0, pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ __m128i side = _mm_slli_epi32(inputSample1, pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ __m128i tempL = _mm_add_epi32(mid, side);
+ __m128i tempR = _mm_sub_epi32(mid, side);
+
+ // Signed bit shift.
+ tempL = _mm_or_si128(_mm_srli_epi32(tempL, 1), _mm_and_si128(tempL, _mm_set1_epi32(0x80000000)));
+ tempR = _mm_or_si128(_mm_srli_epi32(tempR, 1), _mm_and_si128(tempR, _mm_set1_epi32(0x80000000)));
+
+ __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
+ __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
+
+ pOutputSamples[i*8+0] = ((float*)&leftf)[0];
+ pOutputSamples[i*8+1] = ((float*)&rightf)[0];
+ pOutputSamples[i*8+2] = ((float*)&leftf)[1];
+ pOutputSamples[i*8+3] = ((float*)&rightf)[1];
+ pOutputSamples[i*8+4] = ((float*)&leftf)[2];
+ pOutputSamples[i*8+5] = ((float*)&rightf)[2];
+ pOutputSamples[i*8+6] = ((float*)&leftf)[3];
+ pOutputSamples[i*8+7] = ((float*)&rightf)[3];
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)(((mid + side) >> 1) * factor);
+ pOutputSamples[i*2+1] = (float)(((mid - side) >> 1) * factor);
+ }
+ } else {
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i mid = _mm_slli_epi32(inputSample0, pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ __m128i side = _mm_slli_epi32(inputSample1, pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ __m128i tempL = _mm_slli_epi32(_mm_srli_epi32(_mm_add_epi32(mid, side), 1), shift);
+ __m128i tempR = _mm_slli_epi32(_mm_srli_epi32(_mm_sub_epi32(mid, side), 1), shift);
+
+ __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
+ __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
+
+ pOutputSamples[i*8+0] = ((float*)&leftf)[0];
+ pOutputSamples[i*8+1] = ((float*)&rightf)[0];
+ pOutputSamples[i*8+2] = ((float*)&leftf)[1];
+ pOutputSamples[i*8+3] = ((float*)&rightf)[1];
+ pOutputSamples[i*8+4] = ((float*)&leftf)[2];
+ pOutputSamples[i*8+5] = ((float*)&rightf)[2];
+ pOutputSamples[i*8+6] = ((float*)&leftf)[3];
+ pOutputSamples[i*8+7] = ((float*)&rightf)[3];
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << shift) * factor);
+ pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << shift) * factor);
+ }
+ }
+}
+#endif
+
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ // Scalar fallback.
+#if 0
+ drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ int shift0 = (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int shift1 = (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ int tempL0 = pInputSamples0[i*4+0] << shift0;
+ int tempL1 = pInputSamples0[i*4+1] << shift0;
+ int tempL2 = pInputSamples0[i*4+2] << shift0;
+ int tempL3 = pInputSamples0[i*4+3] << shift0;
+
+ int tempR0 = pInputSamples1[i*4+0] << shift1;
+ int tempR1 = pInputSamples1[i*4+1] << shift1;
+ int tempR2 = pInputSamples1[i*4+2] << shift1;
+ int tempR3 = pInputSamples1[i*4+3] << shift1;
+
+ pOutputSamples[i*8+0] = (float)(tempL0 * factor);
+ pOutputSamples[i*8+1] = (float)(tempR0 * factor);
+ pOutputSamples[i*8+2] = (float)(tempL1 * factor);
+ pOutputSamples[i*8+3] = (float)(tempR1 * factor);
+ pOutputSamples[i*8+4] = (float)(tempL2 * factor);
+ pOutputSamples[i*8+5] = (float)(tempR2 * factor);
+ pOutputSamples[i*8+6] = (float)(tempL3 * factor);
+ pOutputSamples[i*8+7] = (float)(tempR3 * factor);
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1.0f / 8388608.0f;
+ __m128 factor128 = _mm_set1_ps(1.0f / 8388608.0f);
+
+ int shift0 = (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample) - 8;
+ int shift1 = (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (drflac_uint64 i = 0; i < frameCount4; ++i) {
+ __m128i inputSample0 = _mm_loadu_si128((const __m128i*)pInputSamples0 + i);
+ __m128i inputSample1 = _mm_loadu_si128((const __m128i*)pInputSamples1 + i);
+
+ __m128i i32L = _mm_slli_epi32(inputSample0, shift0);
+ __m128i i32R = _mm_slli_epi32(inputSample1, shift1);
+
+ __m128 f32L = _mm_mul_ps(_mm_cvtepi32_ps(i32L), factor128);
+ __m128 f32R = _mm_mul_ps(_mm_cvtepi32_ps(i32R), factor128);
+
+ pOutputSamples[i*8+0] = ((float*)&f32L)[0];
+ pOutputSamples[i*8+1] = ((float*)&f32R)[0];
+ pOutputSamples[i*8+2] = ((float*)&f32L)[1];
+ pOutputSamples[i*8+3] = ((float*)&f32R)[1];
+ pOutputSamples[i*8+4] = ((float*)&f32L)[2];
+ pOutputSamples[i*8+5] = ((float*)&f32R)[2];
+ pOutputSamples[i*8+6] = ((float*)&f32L)[3];
+ pOutputSamples[i*8+7] = ((float*)&f32R)[3];
+ }
+
+ for (drflac_uint64 i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ // Scalar fallback.
+#if 0
+ drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut)
+{
+ if (pFlac == NULL || framesToRead == 0) {
+ return 0;
+ }
+
+ if (pBufferOut == NULL) {
+ return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
+ }
+
+ drflac_uint64 framesRead = 0;
+ while (framesToRead > 0) {
+ // If we've run out of samples in this frame, go to the next.
+ if (pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; // Couldn't read the next frame, so just break from the loop and return.
+ }
+ } else {
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint64 totalFramesInPacket = pFlac->currentFrame.header.blockSize;
+ drflac_uint64 framesReadFromPacketSoFar = totalFramesInPacket - (pFlac->currentFrame.samplesRemaining/channelCount);
+ drflac_uint64 iFirstPCMFrame = framesReadFromPacketSoFar;
+ drflac_int32 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+
+ drflac_uint64 frameCountThisIteration = framesToRead;
+ if (frameCountThisIteration > pFlac->currentFrame.samplesRemaining / channelCount) {
+ frameCountThisIteration = pFlac->currentFrame.samplesRemaining / channelCount;
+ }
+
+ if (channelCount == 2) {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + iFirstPCMFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + iFirstPCMFrame;
+
+ switch (pFlac->currentFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+ }
+ } else {
+ // Generic interleaving.
+ for (drflac_uint64 i = 0; i < frameCountThisIteration; ++i) {
+ for (unsigned int j = 0; j < channelCount; ++j) {
+ pBufferOut[(i*channelCount)+j] = (float)(((pFlac->currentFrame.subframes[j].pDecodedSamples[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample)) / 2147483648.0);
+ }
+ }
+ }
+
+ drflac_uint64 samplesReadThisIteration = frameCountThisIteration * channelCount;
+ framesRead += frameCountThisIteration;
+ framesReadFromPacketSoFar += frameCountThisIteration;
+ pBufferOut += samplesReadThisIteration;
+ framesToRead -= frameCountThisIteration;
+ pFlac->currentSample += samplesReadThisIteration;
+ pFlac->currentFrame.samplesRemaining -= (unsigned int)samplesReadThisIteration;
+ }
+ }
+
+ return framesRead;
+}
+
+drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ if (pFlac == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ // If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present
+ // when the decoder was opened.
+ if (pFlac->firstFramePos == 0) {
+ return DRFLAC_FALSE;
+ }
+
+ if (sampleIndex == 0) {
+ pFlac->currentSample = 0;
+ return drflac__seek_to_first_frame(pFlac);
+ } else {
+ drflac_bool32 wasSuccessful = DRFLAC_FALSE;
+
+ // Clamp the sample to the end.
+ if (sampleIndex >= pFlac->totalSampleCount) {
+ sampleIndex = pFlac->totalSampleCount - 1;
+ }
+
+ // If the target sample and the current sample are in the same frame we just move the position forward.
+ if (sampleIndex > pFlac->currentSample) {
+ // Forward.
+ drflac_uint32 offset = (drflac_uint32)(sampleIndex - pFlac->currentSample);
+ if (pFlac->currentFrame.samplesRemaining > offset) {
+ pFlac->currentFrame.samplesRemaining -= offset;
+ pFlac->currentSample = sampleIndex;
+ return DRFLAC_TRUE;
+ }
+ } else {
+ // Backward.
+ drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - sampleIndex);
+ drflac_uint32 currentFrameSampleCount = pFlac->currentFrame.header.blockSize * drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint32 currentFrameSamplesConsumed = (drflac_uint32)(currentFrameSampleCount - pFlac->currentFrame.samplesRemaining);
+ if (currentFrameSamplesConsumed > offsetAbs) {
+ pFlac->currentFrame.samplesRemaining += offsetAbs;
+ pFlac->currentSample = sampleIndex;
+ return DRFLAC_TRUE;
+ }
+ }
+
+ // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so
+ // we'll instead use Ogg's natural seeking facility.
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ wasSuccessful = drflac_ogg__seek_to_sample(pFlac, sampleIndex);
+ }
+ else
+#endif
+ {
+ // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower.
+ wasSuccessful = drflac__seek_to_sample__seek_table(pFlac, sampleIndex);
+ if (!wasSuccessful) {
+ wasSuccessful = drflac__seek_to_sample__brute_force(pFlac, sampleIndex);
+ }
+ }
+
+ pFlac->currentSample = sampleIndex;
+ return wasSuccessful;
+ }
+}
+
+drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
+{
+ if (pFlac == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ // If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present
+ // when the decoder was opened.
+ if (pFlac->firstFramePos == 0) {
+ return DRFLAC_FALSE;
+ }
+
+ if (pcmFrameIndex == 0) {
+ pFlac->currentSample = 0;
+ return drflac__seek_to_first_frame(pFlac);
+ } else {
+ drflac_bool32 wasSuccessful = DRFLAC_FALSE;
+
+ // Clamp the sample to the end.
+ if (pcmFrameIndex >= pFlac->totalPCMFrameCount) {
+ pcmFrameIndex = pFlac->totalPCMFrameCount - 1;
+ }
+
+ // If the target sample and the current sample are in the same frame we just move the position forward.
+ if (pcmFrameIndex*pFlac->channels > pFlac->currentSample) {
+ // Forward.
+ drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex*pFlac->channels - pFlac->currentSample);
+ if (pFlac->currentFrame.samplesRemaining > offset) {
+ pFlac->currentFrame.samplesRemaining -= offset;
+ pFlac->currentSample = pcmFrameIndex*pFlac->channels;
+ return DRFLAC_TRUE;
+ }
+ } else {
+ // Backward.
+ drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - pcmFrameIndex*pFlac->channels);
+ drflac_uint32 currentFrameSampleCount = pFlac->currentFrame.header.blockSize * drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint32 currentFrameSamplesConsumed = (drflac_uint32)(currentFrameSampleCount - pFlac->currentFrame.samplesRemaining);
+ if (currentFrameSamplesConsumed > offsetAbs) {
+ pFlac->currentFrame.samplesRemaining += offsetAbs;
+ pFlac->currentSample = pcmFrameIndex*pFlac->channels;
+ return DRFLAC_TRUE;
+ }
+ }
+
+ // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so
+ // we'll instead use Ogg's natural seeking facility.
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ wasSuccessful = drflac_ogg__seek_to_sample(pFlac, pcmFrameIndex*pFlac->channels);
+ }
+ else
+#endif
+ {
+ // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower.
+ wasSuccessful = drflac__seek_to_sample__seek_table(pFlac, pcmFrameIndex*pFlac->channels);
+ if (!wasSuccessful) {
+ wasSuccessful = drflac__seek_to_sample__brute_force(pFlac, pcmFrameIndex*pFlac->channels);
+ }
+ }
+
+ pFlac->currentSample = pcmFrameIndex*pFlac->channels;
+ return wasSuccessful;
+ }
+}
+
+
+
+//// High Level APIs ////
+
+#if defined(SIZE_MAX)
+ #define DRFLAC_SIZE_MAX SIZE_MAX
+#else
+ #if defined(DRFLAC_64BIT)
+ #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRFLAC_SIZE_MAX 0xFFFFFFFF
+ #endif
+#endif
+
+
+// Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me.
+#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
+static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\
+{ \
+ drflac_assert(pFlac != NULL); \
+ \
+ type* pSampleData = NULL; \
+ drflac_uint64 totalPCMFrameCount = pFlac->totalPCMFrameCount; \
+ \
+ if (totalPCMFrameCount == 0) { \
+ type buffer[4096]; \
+ \
+ size_t sampleDataBufferSize = sizeof(buffer); \
+ pSampleData = (type*)DRFLAC_MALLOC(sampleDataBufferSize); \
+ if (pSampleData == NULL) { \
+ goto on_error; \
+ } \
+ \
+ drflac_uint64 pcmFramesRead; \
+ while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
+ if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
+ sampleDataBufferSize *= 2; \
+ type* pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \
+ if (pNewSampleData == NULL) { \
+ DRFLAC_FREE(pSampleData); \
+ goto on_error; \
+ } \
+ \
+ pSampleData = pNewSampleData; \
+ } \
+ \
+ drflac_copy_memory(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
+ totalPCMFrameCount += pcmFramesRead; \
+ } \
+ \
+ /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \
+ protect those ears from random noise! */ \
+ drflac_zero_memory(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
+ } else { \
+ drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
+ if (dataSize > DRFLAC_SIZE_MAX) { \
+ goto on_error; /* The decoded data is too big. */ \
+ } \
+ \
+ pSampleData = (type*)DRFLAC_MALLOC((size_t)dataSize); /* <-- Safe cast as per the check above. */ \
+ if (pSampleData == NULL) { \
+ goto on_error; \
+ } \
+ \
+ totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
+ } \
+ \
+ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
+ if (channelsOut) *channelsOut = pFlac->channels; \
+ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
+ \
+ drflac_close(pFlac); \
+ return pSampleData; \
+ \
+on_error: \
+ drflac_close(pFlac); \
+ return NULL; \
+}
+
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32)
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
+
+drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = 0;
+
+ drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
+}
+
+drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ drflac_int32* pResult = drflac_open_and_read_pcm_frames_s32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+
+
+
+drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = 0;
+
+ drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
+}
+
+drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ drflac_int16* pResult = drflac_open_and_read_pcm_frames_s16(onRead, onSeek, pUserData, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+
+
+float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = 0;
+
+ drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
+}
+
+float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ float* pResult = drflac_open_and_read_pcm_frames_f32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+
+#ifndef DR_FLAC_NO_STDIO
+drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalPCMFrameCount) *totalPCMFrameCount = 0;
+
+ drflac* pFlac = drflac_open_file(filename);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ drflac_int32* pResult = drflac_open_file_and_read_pcm_frames_s32(filename, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+
+
+drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalPCMFrameCount) *totalPCMFrameCount = 0;
+
+ drflac* pFlac = drflac_open_file(filename);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ drflac_int16* pResult = drflac_open_file_and_read_pcm_frames_s16(filename, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+
+
+float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalPCMFrameCount) *totalPCMFrameCount = 0;
+
+ drflac* pFlac = drflac_open_file(filename);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ float* pResult = drflac_open_file_and_read_pcm_frames_f32(filename, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+#endif
+
+drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalPCMFrameCount) *totalPCMFrameCount = 0;
+
+ drflac* pFlac = drflac_open_memory(data, dataSize);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ drflac_int32* pResult = drflac_open_memory_and_read_pcm_frames_s32(data, dataSize, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+
+
+drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalPCMFrameCount) *totalPCMFrameCount = 0;
+
+ drflac* pFlac = drflac_open_memory(data, dataSize);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ drflac_int16* pResult = drflac_open_memory_and_read_pcm_frames_s16(data, dataSize, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+
+
+float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalPCMFrameCount) *totalPCMFrameCount = 0;
+
+ drflac* pFlac = drflac_open_memory(data, dataSize);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)
+{
+ // Safety.
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalSampleCountOut) *totalSampleCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drflac_uint64 totalPCMFrameCount;
+
+ float* pResult = drflac_open_memory_and_read_pcm_frames_f32(data, dataSize, &channels, &sampleRate, &totalPCMFrameCount);
+ if (pResult == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalSampleCountOut) *totalSampleCountOut = totalPCMFrameCount * channels;
+
+ return pResult;
+}
+
+
+void drflac_free(void* pSampleDataReturnedByOpenAndDecode)
+{
+ DRFLAC_FREE(pSampleDataReturnedByOpenAndDecode);
+}
+
+
+
+
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments)
+{
+ if (pIter == NULL) {
+ return;
+ }
+
+ pIter->countRemaining = commentCount;
+ pIter->pRunningData = (const char*)pComments;
+}
+
+const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut)
+{
+ // Safety.
+ if (pCommentLengthOut) *pCommentLengthOut = 0;
+
+ if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
+ return NULL;
+ }
+
+ drflac_uint32 length = drflac__le2host_32(*(const drflac_uint32*)pIter->pRunningData);
+ pIter->pRunningData += 4;
+
+ const char* pComment = pIter->pRunningData;
+ pIter->pRunningData += length;
+ pIter->countRemaining -= 1;
+
+ if (pCommentLengthOut) *pCommentLengthOut = length;
+ return pComment;
+}
+
+
+
+
+void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData)
+{
+ if (pIter == NULL) {
+ return;
+ }
+
+ pIter->countRemaining = trackCount;
+ pIter->pRunningData = (const char*)pTrackData;
+}
+
+drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack)
+{
+ if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_cuesheet_track cuesheetTrack;
+
+ const char* pRunningData = pIter->pRunningData;
+
+ drflac_uint64 offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ drflac_uint64 offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ cuesheetTrack.offset = offsetLo | (offsetHi << 32);
+ cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
+ drflac_copy_memory(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
+ cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
+ cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
+ cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
+ cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index);
+
+ pIter->pRunningData = pRunningData;
+ pIter->countRemaining -= 1;
+
+ if (pCuesheetTrack) *pCuesheetTrack = cuesheetTrack;
+ return DRFLAC_TRUE;
+}
+#endif //DR_FLAC_IMPLEMENTATION
+
+
+// REVISION HISTORY
+//
+// v0.10.0 - 2018-09-11
+// - Remove the DR_FLAC_NO_WIN32_IO option and the Win32 file IO functionality. If you need to use Win32 file IO you
+// need to do it yourself via the callback API.
+// - Fix the clang build.
+// - Fix undefined behavior.
+// - Fix errors with CUESHEET metdata blocks.
+// - Add an API for iterating over each cuesheet track in the CUESHEET metadata block. This works the same way as the
+// Vorbis comment API.
+// - Other miscellaneous bug fixes, mostly relating to invalid FLAC streams.
+// - Minor optimizations.
+//
+// v0.9.11 - 2018-08-29
+// - Fix a bug with sample reconstruction.
+//
+// v0.9.10 - 2018-08-07
+// - Improve 64-bit detection.
+//
+// v0.9.9 - 2018-08-05
+// - Fix C++ build on older versions of GCC.
+//
+// v0.9.8 - 2018-07-24
+// - Fix compilation errors.
+//
+// v0.9.7 - 2018-07-05
+// - Fix a warning.
+//
+// v0.9.6 - 2018-06-29
+// - Fix some typos.
+//
+// v0.9.5 - 2018-06-23
+// - Fix some warnings.
+//
+// v0.9.4 - 2018-06-14
+// - Optimizations to seeking.
+// - Clean up.
+//
+// v0.9.3 - 2018-05-22
+// - Bug fix.
+//
+// v0.9.2 - 2018-05-12
+// - Fix a compilation error due to a missing break statement.
+//
+// v0.9.1 - 2018-04-29
+// - Fix compilation error with Clang.
+//
+// v0.9 - 2018-04-24
+// - Fix Clang build.
+// - Start using major.minor.revision versioning.
+//
+// v0.8g - 2018-04-19
+// - Fix build on non-x86/x64 architectures.
+//
+// v0.8f - 2018-02-02
+// - Stop pretending to support changing rate/channels mid stream.
+//
+// v0.8e - 2018-02-01
+// - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream.
+// - Fix a crash the the Rice partition order is invalid.
+//
+// v0.8d - 2017-09-22
+// - Add support for decoding streams with ID3 tags. ID3 tags are just skipped.
+//
+// v0.8c - 2017-09-07
+// - Fix warning on non-x86/x64 architectures.
+//
+// v0.8b - 2017-08-19
+// - Fix build on non-x86/x64 architectures.
+//
+// v0.8a - 2017-08-13
+// - A small optimization for the Clang build.
+//
+// v0.8 - 2017-08-12
+// - API CHANGE: Rename dr_* types to drflac_*.
+// - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation.
+// - Add support for custom implementations of malloc(), realloc(), etc.
+// - Add CRC checking to Ogg encapsulated streams.
+// - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported.
+// - Bug fixes.
+//
+// v0.7 - 2017-07-23
+// - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed().
+//
+// v0.6 - 2017-07-22
+// - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they
+// never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame.
+//
+// v0.5 - 2017-07-16
+// - Fix typos.
+// - Change drflac_bool* types to unsigned.
+// - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC.
+//
+// v0.4f - 2017-03-10
+// - Fix a couple of bugs with the bitstreaming code.
+//
+// v0.4e - 2017-02-17
+// - Fix some warnings.
+//
+// v0.4d - 2016-12-26
+// - Add support for 32-bit floating-point PCM decoding.
+// - Use drflac_int*/drflac_uint* sized types to improve compiler support.
+// - Minor improvements to documentation.
+//
+// v0.4c - 2016-12-26
+// - Add support for signed 16-bit integer PCM decoding.
+//
+// v0.4b - 2016-10-23
+// - A minor change to drflac_bool8 and drflac_bool32 types.
+//
+// v0.4a - 2016-10-11
+// - Rename drBool32 to drflac_bool32 for styling consistency.
+//
+// v0.4 - 2016-09-29
+// - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type.
+// - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32().
+// - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to
+// keep it consistent with drflac_audio.
+//
+// v0.3f - 2016-09-21
+// - Fix a warning with GCC.
+//
+// v0.3e - 2016-09-18
+// - Fixed a bug where GCC 4.3+ was not getting properly identified.
+// - Fixed a few typos.
+// - Changed date formats to ISO 8601 (YYYY-MM-DD).
+//
+// v0.3d - 2016-06-11
+// - Minor clean up.
+//
+// v0.3c - 2016-05-28
+// - Fixed compilation error.
+//
+// v0.3b - 2016-05-16
+// - Fixed Linux/GCC build.
+// - Updated documentation.
+//
+// v0.3a - 2016-05-15
+// - Minor fixes to documentation.
+//
+// v0.3 - 2016-05-11
+// - Optimizations. Now at about parity with the reference implementation on 32-bit builds.
+// - Lots of clean up.
+//
+// v0.2b - 2016-05-10
+// - Bug fixes.
+//
+// v0.2a - 2016-05-10
+// - Made drflac_open_and_decode() more robust.
+// - Removed an unused debugging variable
+//
+// v0.2 - 2016-05-09
+// - Added support for Ogg encapsulation.
+// - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek
+// should be relative to the start or the current position. Also changes the seeking rules such that
+// seeking offsets will never be negative.
+// - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count.
+//
+// v0.1b - 2016-05-07
+// - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize.
+// - Removed a stale comment.
+//
+// v0.1a - 2016-05-05
+// - Minor formatting changes.
+// - Fixed a warning on the GCC build.
+//
+// v0.1 - 2016-05-03
+// - Initial versioned release.
+
+
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+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 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.
+
+For more information, please refer to
+*/
diff --git a/src/soloud/src/audiosource/wav/dr_impl.cpp b/src/soloud/src/audiosource/wav/dr_impl.cpp
new file mode 100644
index 0000000..fa152e1
--- /dev/null
+++ b/src/soloud/src/audiosource/wav/dr_impl.cpp
@@ -0,0 +1,13 @@
+#define DR_MP3_IMPLEMENTATION
+#define DR_MP3_NO_STDIO
+#define DR_MP3_FLOAT_OUTPUT
+#include "dr_mp3.h"
+
+#define DR_WAV_IMPLEMENTATION
+#define DR_WAV_NO_STDIO
+#include "dr_wav.h"
+
+#define DR_FLAC_IMPLEMENTATION
+#define DR_FLAC_NO_STDIO
+#define DR_FLAC_NO_CRC
+#include "dr_flac.h"
diff --git a/src/soloud/src/audiosource/wav/dr_mp3.h b/src/soloud/src/audiosource/wav/dr_mp3.h
new file mode 100644
index 0000000..070f0c1
--- /dev/null
+++ b/src/soloud/src/audiosource/wav/dr_mp3.h
@@ -0,0 +1,3200 @@
+// MP3 audio decoder. Public domain. See "unlicense" statement at the end of this file.
+// dr_mp3 - v0.4.0 - 2018-xx-xx
+//
+// David Reid - mackron@gmail.com
+//
+// Based off minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for
+// differences between minimp3 and dr_mp3.
+
+// USAGE
+// =====
+// dr_mp3 is a single-file library. To use it, do something like the following in one .c file.
+// #define DR_MP3_IMPLEMENTATION
+// #include "dr_mp3.h"
+//
+// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data,
+// do something like the following:
+//
+// drmp3 mp3;
+// if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) {
+// // Failed to open file
+// }
+//
+// ...
+//
+// drmp3_uint64 framesRead = drmp3_read_f32(pMP3, framesToRead, pFrames);
+//
+// The drmp3 object is transparent so you can get access to the channel count and sample rate like so:
+//
+// drmp3_uint32 channels = mp3.channels;
+// drmp3_uint32 sampleRate = mp3.sampleRate;
+//
+// The third parameter of drmp3_init_file() in the example above allows you to control the output channel count and sample rate. It
+// is a pointer to a drmp3_config object. Setting any of the variables of this object to 0 will cause dr_mp3 to use defaults.
+//
+// The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek
+// callbacks with drmp3_init_memory() and drmp3_init() respectively.
+//
+// You do need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request
+// any number of PCM frames in each call to drmp3_read_f32() and it will return as many PCM frames as it can, up to the requested
+// amount.
+//
+// You can also decode an entire file in one go with drmp3_open_and_decode_f32(), drmp3_open_and_decode_memory_f32() and
+// drmp3_open_and_decode_file_f32().
+//
+//
+// OPTIONS
+// =======
+// #define these options before including this file.
+//
+// #define DR_MP3_NO_STDIO
+// Disable drmp3_init_file(), etc.
+//
+// #define DR_MP3_NO_SIMD
+// Disable SIMD optimizations.
+
+#ifndef dr_mp3_h
+#define dr_mp3_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+typedef signed char drmp3_int8;
+typedef unsigned char drmp3_uint8;
+typedef signed short drmp3_int16;
+typedef unsigned short drmp3_uint16;
+typedef signed int drmp3_int32;
+typedef unsigned int drmp3_uint32;
+typedef signed __int64 drmp3_int64;
+typedef unsigned __int64 drmp3_uint64;
+#else
+#include
+typedef int8_t drmp3_int8;
+typedef uint8_t drmp3_uint8;
+typedef int16_t drmp3_int16;
+typedef uint16_t drmp3_uint16;
+typedef int32_t drmp3_int32;
+typedef uint32_t drmp3_uint32;
+typedef int64_t drmp3_int64;
+typedef uint64_t drmp3_uint64;
+#endif
+typedef drmp3_uint8 drmp3_bool8;
+typedef drmp3_uint32 drmp3_bool32;
+#define DRMP3_TRUE 1
+#define DRMP3_FALSE 0
+
+#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
+#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
+
+
+// Low Level Push API
+// ==================
+typedef struct
+{
+ int frame_bytes, channels, hz, layer, bitrate_kbps;
+} drmp3dec_frame_info;
+
+typedef struct
+{
+ float mdct_overlap[2][9*32], qmf_state[15*2*32];
+ int reserv, free_format_bytes;
+ unsigned char header[4], reserv_buf[511];
+} drmp3dec;
+
+// Initializes a low level decoder.
+void drmp3dec_init(drmp3dec *dec);
+
+// Reads a frame from a low level decoder.
+int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
+
+// Helper for converting between f32 and s16.
+void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples);
+
+
+
+
+// Main API (Pull API)
+// ===================
+
+typedef struct drmp3_src drmp3_src;
+typedef drmp3_uint64 (* drmp3_src_read_proc)(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read.
+
+typedef enum
+{
+ drmp3_src_algorithm_none,
+ drmp3_src_algorithm_linear
+} drmp3_src_algorithm;
+
+#define DRMP3_SRC_CACHE_SIZE_IN_FRAMES 512
+typedef struct
+{
+ drmp3_src* pSRC;
+ float pCachedFrames[2 * DRMP3_SRC_CACHE_SIZE_IN_FRAMES];
+ drmp3_uint32 cachedFrameCount;
+ drmp3_uint32 iNextFrame;
+} drmp3_src_cache;
+
+typedef struct
+{
+ drmp3_uint32 sampleRateIn;
+ drmp3_uint32 sampleRateOut;
+ drmp3_uint32 channels;
+ drmp3_src_algorithm algorithm;
+ drmp3_uint32 cacheSizeInFrames; // The number of frames to read from the client at a time.
+} drmp3_src_config;
+
+struct drmp3_src
+{
+ drmp3_src_config config;
+ drmp3_src_read_proc onRead;
+ void* pUserData;
+ float bin[256];
+ drmp3_src_cache cache; // <-- For simplifying and optimizing client -> memory reading.
+ union
+ {
+ struct
+ {
+ float alpha;
+ drmp3_bool32 isPrevFramesLoaded : 1;
+ drmp3_bool32 isNextFramesLoaded : 1;
+ } linear;
+ } algo;
+};
+
+typedef enum
+{
+ drmp3_seek_origin_start,
+ drmp3_seek_origin_current
+} drmp3_seek_origin;
+
+// Callback for when data is read. Return value is the number of bytes actually read.
+//
+// pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
+// pBufferOut [out] The output buffer.
+// bytesToRead [in] The number of bytes to read.
+//
+// Returns the number of bytes actually read.
+//
+// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+// either the entire bytesToRead is filled or you have reached the end of the stream.
+typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+// Callback for when data needs to be seeked.
+//
+// pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
+// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
+// origin [in] The origin of the seek - the current position or the start of the stream.
+//
+// Returns whether or not the seek was successful.
+//
+// Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
+// will be either drmp3_seek_origin_start or drmp3_seek_origin_current.
+typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
+
+typedef struct
+{
+ drmp3_uint32 outputChannels;
+ drmp3_uint32 outputSampleRate;
+} drmp3_config;
+
+typedef struct
+{
+ drmp3dec decoder;
+ drmp3dec_frame_info frameInfo;
+ drmp3_uint32 channels;
+ drmp3_uint32 sampleRate;
+ drmp3_read_proc onRead;
+ drmp3_seek_proc onSeek;
+ void* pUserData;
+ drmp3_uint32 mp3FrameChannels; // The number of channels in the currently loaded MP3 frame. Internal use only.
+ drmp3_uint32 mp3FrameSampleRate; // The sample rate of the currently loaded MP3 frame. Internal use only.
+ drmp3_uint32 pcmFramesConsumedInMP3Frame;
+ drmp3_uint32 pcmFramesRemainingInMP3Frame;
+ drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; // <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT.
+ drmp3_uint64 currentPCMFrame; // The current PCM frame, globally, based on the output sample rate. Mainly used for seeking.
+ drmp3_src src;
+ size_t dataSize;
+ size_t dataCapacity;
+ drmp3_uint8* pData;
+ drmp3_bool32 atEnd : 1;
+ struct
+ {
+ const drmp3_uint8* pData;
+ size_t dataSize;
+ size_t currentReadPos;
+ } memory; // Only used for decoders that were opened against a block of memory.
+} drmp3;
+
+// Initializes an MP3 decoder.
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns true if successful; false otherwise.
+//
+// Close the loader with drmp3_uninit().
+//
+// See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit()
+drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig);
+
+// Initializes an MP3 decoder from a block of memory.
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the drmp3 object.
+//
+// The buffer should contain the contents of the entire MP3 file.
+drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig);
+
+#ifndef DR_MP3_NO_STDIO
+// Initializes an MP3 decoder from a file.
+//
+// This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig);
+#endif
+
+// Uninitializes an MP3 decoder.
+void drmp3_uninit(drmp3* pMP3);
+
+// Reads PCM frames as interleaved 32-bit IEEE floating point PCM.
+//
+// Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
+drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
+
+// Seeks to a specific frame.
+//
+// Note that this is _not_ an MP3 frame, but rather a PCM frame.
+drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
+
+// Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
+// radio. Runs in linear time. Returns 0 on error.
+drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
+
+// Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams such as internet
+// radio. Runs in linear time. Returns 0 on error.
+drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
+
+
+
+// Opens an decodes an entire MP3 stream as a single operation.
+//
+// pConfig is both an input and output. On input it contains what you want. On output it contains what you got.
+//
+// Free the returned pointer with drmp3_free().
+float* drmp3_open_and_read_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
+float* drmp3_open_memory_and_read_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
+#ifndef DR_MP3_NO_STDIO
+float* drmp3_open_file_and_read_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount);
+#endif
+
+// Frees any memory that was allocated by a public drmp3 API.
+void drmp3_free(void* p);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // dr_mp3_h
+
+
+/////////////////////////////////////////////////////
+//
+// IMPLEMENTATION
+//
+/////////////////////////////////////////////////////
+#ifdef DR_MP3_IMPLEMENTATION
+#include
+#include
+#include
+#include // For INT_MAX
+
+// Disable SIMD when compiling with TCC for now.
+#if defined(__TINYC__)
+#define DR_MP3_NO_SIMD
+#endif
+
+#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
+
+#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */
+#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
+#define DRMP3_MAX_FRAME_SYNC_MATCHES 10
+#endif
+
+#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */
+
+#define DRMP3_MAX_BITRESERVOIR_BYTES 511
+#define DRMP3_SHORT_BLOCK_TYPE 2
+#define DRMP3_STOP_BLOCK_TYPE 3
+#define DRMP3_MODE_MONO 3
+#define DRMP3_MODE_JOINT_STEREO 1
+#define DRMP3_HDR_SIZE 4
+#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0)
+#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60)
+#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0)
+#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1))
+#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2)
+#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8)
+#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10)
+#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10)
+#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20)
+#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3)
+#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3)
+#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3)
+#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4)
+#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3)
+#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3)
+#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2)
+#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6)
+
+#define DRMP3_BITS_DEQUANTIZER_OUT -1
+#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210)
+#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3)
+
+#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a))
+#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a))
+
+#if !defined(DR_MP3_NO_SIMD)
+
+#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(_M_ARM64) || defined(__x86_64__) || defined(__aarch64__))
+/* x64 always have SSE2, arm64 always have neon, no need for generic code */
+#define DR_MP3_ONLY_SIMD
+#endif
+
+#if (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
+#if defined(_MSC_VER)
+#include
+#endif
+#include
+#define DRMP3_HAVE_SSE 1
+#define DRMP3_HAVE_SIMD 1
+#define DRMP3_VSTORE _mm_storeu_ps
+#define DRMP3_VLD _mm_loadu_ps
+#define DRMP3_VSET _mm_set1_ps
+#define DRMP3_VADD _mm_add_ps
+#define DRMP3_VSUB _mm_sub_ps
+#define DRMP3_VMUL _mm_mul_ps
+#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))
+#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))
+#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
+#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
+typedef __m128 drmp3_f4;
+#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)
+#define drmp3_cpuid __cpuid
+#else
+static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType)
+{
+#if defined(__PIC__)
+ __asm__ __volatile__(
+#if defined(__x86_64__)
+ "push %%rbx\n"
+ "cpuid\n"
+ "xchgl %%ebx, %1\n"
+ "pop %%rbx\n"
+#else
+ "xchgl %%ebx, %1\n"
+ "cpuid\n"
+ "xchgl %%ebx, %1\n"
+#endif
+ : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
+ : "a" (InfoType));
+#else
+ __asm__ __volatile__(
+ "cpuid"
+ : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
+ : "a" (InfoType));
+#endif
+}
+#endif
+static int drmp3_have_simd()
+{
+#ifdef DR_MP3_ONLY_SIMD
+ return 1;
+#else
+ static int g_have_simd;
+ int CPUInfo[4];
+#ifdef MINIMP3_TEST
+ static int g_counter;
+ if (g_counter++ > 100)
+ return 0;
+#endif
+ if (g_have_simd)
+ goto end;
+ drmp3_cpuid(CPUInfo, 0);
+ if (CPUInfo[0] > 0)
+ {
+ drmp3_cpuid(CPUInfo, 1);
+ g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */
+ return g_have_simd - 1;
+ }
+
+end:
+ return g_have_simd - 1;
+#endif
+}
+#elif defined(__ARM_NEON) || defined(__aarch64__)
+#include
+#define DRMP3_HAVE_SIMD 1
+#define DRMP3_VSTORE vst1q_f32
+#define DRMP3_VLD vld1q_f32
+#define DRMP3_VSET vmovq_n_f32
+#define DRMP3_VADD vaddq_f32
+#define DRMP3_VSUB vsubq_f32
+#define DRMP3_VMUL vmulq_f32
+#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y)
+#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y)
+#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
+#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
+typedef float32x4_t drmp3_f4;
+static int drmp3_have_simd()
+{ /* TODO: detect neon for !DR_MP3_ONLY_SIMD */
+ return 1;
+}
+#else
+#define DRMP3_HAVE_SIMD 0
+#ifdef DR_MP3_ONLY_SIMD
+#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
+#endif
+#endif
+
+#else
+
+#define DRMP3_HAVE_SIMD 0
+
+#endif
+
+typedef struct
+{
+ const drmp3_uint8 *buf;
+ int pos, limit;
+} drmp3_bs;
+
+typedef struct
+{
+ float scf[3*64];
+ drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
+} drmp3_L12_scale_info;
+
+typedef struct
+{
+ drmp3_uint8 tab_offset, code_tab_width, band_count;
+} drmp3_L12_subband_alloc;
+
+typedef struct
+{
+ const drmp3_uint8 *sfbtab;
+ drmp3_uint16 part_23_length, big_values, scalefac_compress;
+ drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
+ drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
+ drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
+} drmp3_L3_gr_info;
+
+typedef struct
+{
+ drmp3_bs bs;
+ drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
+ drmp3_L3_gr_info gr_info[4];
+ float grbuf[2][576], scf[40], syn[18 + 15][2*32];
+ drmp3_uint8 ist_pos[2][39];
+} drmp3dec_scratch;
+
+static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes)
+{
+ bs->buf = data;
+ bs->pos = 0;
+ bs->limit = bytes*8;
+}
+
+static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n)
+{
+ drmp3_uint32 next, cache = 0, s = bs->pos & 7;
+ int shl = n + s;
+ const drmp3_uint8 *p = bs->buf + (bs->pos >> 3);
+ if ((bs->pos += n) > bs->limit)
+ return 0;
+ next = *p++ & (255 >> s);
+ while ((shl -= 8) > 0)
+ {
+ cache |= next << shl;
+ next = *p++;
+ }
+ return cache | (next >> -shl);
+}
+
+static int drmp3_hdr_valid(const drmp3_uint8 *h)
+{
+ return h[0] == 0xff &&
+ ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
+ (DRMP3_HDR_GET_LAYER(h) != 0) &&
+ (DRMP3_HDR_GET_BITRATE(h) != 15) &&
+ (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3);
+}
+
+static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2)
+{
+ return drmp3_hdr_valid(h2) &&
+ ((h1[1] ^ h2[1]) & 0xFE) == 0 &&
+ ((h1[2] ^ h2[2]) & 0x0C) == 0 &&
+ !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2));
+}
+
+static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h)
+{
+ static const drmp3_uint8 halfrate[2][3][15] = {
+ { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } },
+ { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } },
+ };
+ return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)];
+}
+
+static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h)
+{
+ static const unsigned g_hz[3] = { 44100, 48000, 32000 };
+ return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h);
+}
+
+static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h)
+{
+ return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h));
+}
+
+static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size)
+{
+ int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h);
+ if (DRMP3_HDR_IS_LAYER_1(h))
+ {
+ frame_bytes &= ~3; /* slot align */
+ }
+ return frame_bytes ? frame_bytes : free_format_size;
+}
+
+static int drmp3_hdr_padding(const drmp3_uint8 *h)
+{
+ return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;
+}
+
+#ifndef DR_MP3_ONLY_MP3
+static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci)
+{
+ const drmp3_L12_subband_alloc *alloc;
+ int mode = DRMP3_HDR_GET_STEREO_MODE(hdr);
+ int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32;
+
+ if (DRMP3_HDR_IS_LAYER_1(hdr))
+ {
+ static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };
+ alloc = g_alloc_L1;
+ nbands = 32;
+ } else if (!DRMP3_HDR_TEST_MPEG1(hdr))
+ {
+ static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };
+ alloc = g_alloc_L2M2;
+ nbands = 30;
+ } else
+ {
+ static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };
+ int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr);
+ unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO);
+ if (!kbps) /* free-format */
+ {
+ kbps = 192;
+ }
+
+ alloc = g_alloc_L2M1;
+ nbands = 27;
+ if (kbps < 56)
+ {
+ static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };
+ alloc = g_alloc_L2M1_lowrate;
+ nbands = sample_rate_idx == 2 ? 12 : 8;
+ } else if (kbps >= 96 && sample_rate_idx != 1)
+ {
+ nbands = 30;
+ }
+ }
+
+ sci->total_bands = (drmp3_uint8)nbands;
+ sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands);
+
+ return alloc;
+}
+
+static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf)
+{
+ static const float g_deq_L12[18*3] = {
+#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x
+ DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9)
+ };
+ int i, m;
+ for (i = 0; i < bands; i++)
+ {
+ float s = 0;
+ int ba = *pba++;
+ int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;
+ for (m = 4; m; m >>= 1)
+ {
+ if (mask & m)
+ {
+ int b = drmp3_bs_get_bits(bs, 6);
+ s = g_deq_L12[ba*3 - 6 + b % 3]*(1 << 21 >> b/3);
+ }
+ *scf++ = s;
+ }
+ }
+}
+
+static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci)
+{
+ static const drmp3_uint8 g_bitalloc_code_tab[] = {
+ 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,
+ 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,
+ 0,17,18, 3,19,4,5,16,
+ 0,17,18,16,
+ 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,
+ 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,
+ 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16
+ };
+ const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci);
+
+ int i, k = 0, ba_bits = 0;
+ const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab;
+
+ for (i = 0; i < sci->total_bands; i++)
+ {
+ drmp3_uint8 ba;
+ if (i == k)
+ {
+ k += subband_alloc->band_count;
+ ba_bits = subband_alloc->code_tab_width;
+ ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;
+ subband_alloc++;
+ }
+ ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
+ sci->bitalloc[2*i] = ba;
+ if (i < sci->stereo_bands)
+ {
+ ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)];
+ }
+ sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;
+ }
+
+ for (i = 0; i < 2*sci->total_bands; i++)
+ {
+ sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6);
+ }
+
+ drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);
+
+ for (i = sci->stereo_bands; i < sci->total_bands; i++)
+ {
+ sci->bitalloc[2*i + 1] = 0;
+ }
+}
+
+static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size)
+{
+ int i, j, k, choff = 576;
+ for (j = 0; j < 4; j++)
+ {
+ float *dst = grbuf + group_size*j;
+ for (i = 0; i < 2*sci->total_bands; i++)
+ {
+ int ba = sci->bitalloc[i];
+ if (ba != 0)
+ {
+ if (ba < 17)
+ {
+ int half = (1 << (ba - 1)) - 1;
+ for (k = 0; k < group_size; k++)
+ {
+ dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half);
+ }
+ } else
+ {
+ unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */
+ unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */
+ for (k = 0; k < group_size; k++, code /= mod)
+ {
+ dst[k] = (float)((int)(code % mod - mod/2));
+ }
+ }
+ }
+ dst += choff;
+ choff = 18 - choff;
+ }
+ }
+ return group_size*4;
+}
+
+static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst)
+{
+ int i, k;
+ memcpy(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));
+ for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
+ {
+ for (k = 0; k < 12; k++)
+ {
+ dst[k + 0] *= scf[0];
+ dst[k + 576] *= scf[3];
+ }
+ }
+}
+#endif
+
+static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
+{
+ static const drmp3_uint8 g_scf_long[8][23] = {
+ { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
+ { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },
+ { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
+ { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },
+ { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
+ { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },
+ { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },
+ { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }
+ };
+ static const drmp3_uint8 g_scf_short[8][40] = {
+ { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },
+ { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 },
+ { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 },
+ { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 },
+ { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },
+ { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 },
+ { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 },
+ { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 }
+ };
+ static const drmp3_uint8 g_scf_mixed[8][40] = {
+ { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },
+ { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 },
+ { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 },
+ { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 },
+ { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },
+ { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 },
+ { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 },
+ { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 }
+ };
+
+ unsigned tables, scfsi = 0;
+ int main_data_begin, part_23_sum = 0;
+ int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
+ int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
+
+ if (DRMP3_HDR_TEST_MPEG1(hdr))
+ {
+ gr_count *= 2;
+ main_data_begin = drmp3_bs_get_bits(bs, 9);
+ scfsi = drmp3_bs_get_bits(bs, 7 + gr_count);
+ } else
+ {
+ main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;
+ }
+
+ do
+ {
+ if (DRMP3_HDR_IS_MONO(hdr))
+ {
+ scfsi <<= 4;
+ }
+ gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12);
+ part_23_sum += gr->part_23_length;
+ gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9);
+ if (gr->big_values > 288)
+ {
+ return -1;
+ }
+ gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8);
+ gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);
+ gr->sfbtab = g_scf_long[sr_idx];
+ gr->n_long_sfb = 22;
+ gr->n_short_sfb = 0;
+ if (drmp3_bs_get_bits(bs, 1))
+ {
+ gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2);
+ if (!gr->block_type)
+ {
+ return -1;
+ }
+ gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
+ gr->region_count[0] = 7;
+ gr->region_count[1] = 255;
+ if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE)
+ {
+ scfsi &= 0x0F0F;
+ if (!gr->mixed_block_flag)
+ {
+ gr->region_count[0] = 8;
+ gr->sfbtab = g_scf_short[sr_idx];
+ gr->n_long_sfb = 0;
+ gr->n_short_sfb = 39;
+ } else
+ {
+ gr->sfbtab = g_scf_mixed[sr_idx];
+ gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;
+ gr->n_short_sfb = 30;
+ }
+ }
+ tables = drmp3_bs_get_bits(bs, 10);
+ tables <<= 5;
+ gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
+ gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
+ gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
+ } else
+ {
+ gr->block_type = 0;
+ gr->mixed_block_flag = 0;
+ tables = drmp3_bs_get_bits(bs, 15);
+ gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4);
+ gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3);
+ gr->region_count[2] = 255;
+ }
+ gr->table_select[0] = (drmp3_uint8)(tables >> 10);
+ gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31);
+ gr->table_select[2] = (drmp3_uint8)((tables) & 31);
+ gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));
+ gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
+ gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1);
+ gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15);
+ scfsi <<= 4;
+ gr++;
+ } while(--gr_count);
+
+ if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)
+ {
+ return -1;
+ }
+
+ return main_data_begin;
+}
+
+static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi)
+{
+ int i, k;
+ for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)
+ {
+ int cnt = scf_count[i];
+ if (scfsi & 8)
+ {
+ memcpy(scf, ist_pos, cnt);
+ } else
+ {
+ int bits = scf_size[i];
+ if (!bits)
+ {
+ memset(scf, 0, cnt);
+ memset(ist_pos, 0, cnt);
+ } else
+ {
+ int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
+ for (k = 0; k < cnt; k++)
+ {
+ int s = drmp3_bs_get_bits(bitbuf, bits);
+ ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s);
+ scf[k] = (drmp3_uint8)s;
+ }
+ }
+ }
+ ist_pos += cnt;
+ scf += cnt;
+ }
+ scf[0] = scf[1] = scf[2] = 0;
+}
+
+static float drmp3_L3_ldexp_q2(float y, int exp_q2)
+{
+ static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };
+ int e;
+ do
+ {
+ e = DRMP3_MIN(30*4, exp_q2);
+ y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));
+ } while ((exp_q2 -= e) > 0);
+ return y;
+}
+
+static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch)
+{
+ static const drmp3_uint8 g_scf_partitions[3][28] = {
+ { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 },
+ { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 },
+ { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 }
+ };
+ const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];
+ drmp3_uint8 scf_size[4], iscf[40];
+ int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;
+ float gain;
+
+ if (DRMP3_HDR_TEST_MPEG1(hdr))
+ {
+ static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };
+ int part = g_scfc_decode[gr->scalefac_compress];
+ scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2);
+ scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3);
+ } else
+ {
+ static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 };
+ int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch;
+ sfc = gr->scalefac_compress >> ist;
+ for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)
+ {
+ for (modprod = 1, i = 3; i >= 0; i--)
+ {
+ scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]);
+ modprod *= g_mod[k + i];
+ }
+ }
+ scf_partition += k;
+ scfsi = -16;
+ }
+ drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);
+
+ if (gr->n_short_sfb)
+ {
+ int sh = 3 - scf_shift;
+ for (i = 0; i < gr->n_short_sfb; i += 3)
+ {
+ iscf[gr->n_long_sfb + i + 0] += gr->subblock_gain[0] << sh;
+ iscf[gr->n_long_sfb + i + 1] += gr->subblock_gain[1] << sh;
+ iscf[gr->n_long_sfb + i + 2] += gr->subblock_gain[2] << sh;
+ }
+ } else if (gr->preflag)
+ {
+ static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
+ for (i = 0; i < 10; i++)
+ {
+ iscf[11 + i] += g_preamp[i];
+ }
+ }
+
+ gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
+ gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp);
+ for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
+ {
+ scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
+ }
+}
+
+static const float g_drmp3_pow43[129 + 16] = {
+ 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f,
+ 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f
+};
+
+static float drmp3_L3_pow_43(int x)
+{
+ float frac;
+ int sign, mult = 256;
+
+ if (x < 129)
+ {
+ return g_drmp3_pow43[16 + x];
+ }
+
+ if (x < 1024)
+ {
+ mult = 16;
+ x <<= 3;
+ }
+
+ sign = 2*x & 64;
+ frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
+ return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
+}
+
+static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
+{
+ static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,
+ -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288,
+ -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288,
+ -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258,
+ -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259,
+ -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258,
+ -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258,
+ -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259,
+ -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258,
+ -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290,
+ -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259,
+ -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258,
+ -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259,
+ -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258,
+ -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 };
+ static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205};
+ static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
+ static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 };
+ static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 };
+
+#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n))
+#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
+#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
+#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
+
+ float one = 0.0f;
+ int ireg = 0, big_val_cnt = gr_info->big_values;
+ const drmp3_uint8 *sfb = gr_info->sfbtab;
+ const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
+ drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7);
+ int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
+ bs_next_ptr += 4;
+
+ while (big_val_cnt > 0)
+ {
+ int tab_num = gr_info->table_select[ireg];
+ int sfb_cnt = gr_info->region_count[ireg++];
+ const drmp3_int16 *codebook = tabs + tabindex[tab_num];
+ int linbits = g_linbits[tab_num];
+ do
+ {
+ np = *sfb++ / 2;
+ pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
+ one = *scf++;
+ do
+ {
+ int j, w = 5;
+ int leaf = codebook[DRMP3_PEEK_BITS(w)];
+ while (leaf < 0)
+ {
+ DRMP3_FLUSH_BITS(w);
+ w = leaf & 7;
+ leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
+ }
+ DRMP3_FLUSH_BITS(leaf >> 8);
+
+ for (j = 0; j < 2; j++, dst++, leaf >>= 4)
+ {
+ int lsb = leaf & 0x0F;
+ if (lsb == 15 && linbits)
+ {
+ lsb += DRMP3_PEEK_BITS(linbits);
+ DRMP3_FLUSH_BITS(linbits);
+ DRMP3_CHECK_BITS;
+ *dst = one*drmp3_L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1);
+ } else
+ {
+ *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
+ }
+ DRMP3_FLUSH_BITS(lsb ? 1 : 0);
+ }
+ DRMP3_CHECK_BITS;
+ } while (--pairs_to_decode);
+ } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
+ }
+
+ for (np = 1 - big_val_cnt;; dst += 4)
+ {
+ const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;
+ int leaf = codebook_count1[DRMP3_PEEK_BITS(4)];
+ if (!(leaf & 8))
+ {
+ leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];
+ }
+ DRMP3_FLUSH_BITS(leaf & 7);
+ if (DRMP3_BSPOS > layer3gr_limit)
+ {
+ break;
+ }
+#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }
+#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) }
+ DRMP3_RELOAD_SCALEFACTOR;
+ DRMP3_DEQ_COUNT1(0);
+ DRMP3_DEQ_COUNT1(1);
+ DRMP3_RELOAD_SCALEFACTOR;
+ DRMP3_DEQ_COUNT1(2);
+ DRMP3_DEQ_COUNT1(3);
+ DRMP3_CHECK_BITS;
+ }
+
+ bs->pos = layer3gr_limit;
+}
+
+static void drmp3_L3_midside_stereo(float *left, int n)
+{
+ int i = 0;
+ float *right = left + 576;
+#if DRMP3_HAVE_SIMD
+ if (drmp3_have_simd()) for (; i < n - 3; i += 4)
+ {
+ drmp3_f4 vl = DRMP3_VLD(left + i);
+ drmp3_f4 vr = DRMP3_VLD(right + i);
+ DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr));
+ DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr));
+ }
+#endif
+ for (; i < n; i++)
+ {
+ float a = left[i];
+ float b = right[i];
+ left[i] = a + b;
+ right[i] = a - b;
+ }
+}
+
+static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ {
+ left[i + 576] = left[i]*kr;
+ left[i] = left[i]*kl;
+ }
+}
+
+static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3])
+{
+ int i, k;
+
+ max_band[0] = max_band[1] = max_band[2] = -1;
+
+ for (i = 0; i < nbands; i++)
+ {
+ for (k = 0; k < sfb[i]; k += 2)
+ {
+ if (right[k] != 0 || right[k + 1] != 0)
+ {
+ max_band[i % 3] = i;
+ break;
+ }
+ }
+ right += sfb[i];
+ }
+}
+
+static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh)
+{
+ static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 };
+ unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;
+
+ for (i = 0; sfb[i]; i++)
+ {
+ unsigned ipos = ist_pos[i];
+ if ((int)i > max_band[i % 3] && ipos < max_pos)
+ {
+ float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
+ if (DRMP3_HDR_TEST_MPEG1(hdr))
+ {
+ kl = g_pan[2*ipos];
+ kr = g_pan[2*ipos + 1];
+ } else
+ {
+ kl = 1;
+ kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);
+ if (ipos & 1)
+ {
+ kl = kr;
+ kr = 1;
+ }
+ }
+ drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);
+ } else if (DRMP3_HDR_TEST_MS_STEREO(hdr))
+ {
+ drmp3_L3_midside_stereo(left, sfb[i]);
+ }
+ left += sfb[i];
+ }
+}
+
+static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr)
+{
+ int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;
+ int i, max_blocks = gr->n_short_sfb ? 3 : 1;
+
+ drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);
+ if (gr->n_long_sfb)
+ {
+ max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]);
+ }
+ for (i = 0; i < max_blocks; i++)
+ {
+ int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;
+ int itop = n_sfb - max_blocks + i;
+ int prev = itop - max_blocks;
+ ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);
+ }
+ drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);
+}
+
+static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb)
+{
+ int i, len;
+ float *src = grbuf, *dst = scratch;
+
+ for (;0 != (len = *sfb); sfb += 3, src += 2*len)
+ {
+ for (i = 0; i < len; i++, src++)
+ {
+ *dst++ = src[0*len];
+ *dst++ = src[1*len];
+ *dst++ = src[2*len];
+ }
+ }
+ memcpy(grbuf, scratch, (dst - scratch)*sizeof(float));
+}
+
+static void drmp3_L3_antialias(float *grbuf, int nbands)
+{
+ static const float g_aa[2][8] = {
+ {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},
+ {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}
+ };
+
+ for (; nbands > 0; nbands--, grbuf += 18)
+ {
+ int i = 0;
+#if DRMP3_HAVE_SIMD
+ if (drmp3_have_simd()) for (; i < 8; i += 4)
+ {
+ drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i);
+ drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i);
+ drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i);
+ drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i);
+ vd = DRMP3_VREV(vd);
+ DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1)));
+ vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0));
+ DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd));
+ }
+#endif
+#ifndef DR_MP3_ONLY_SIMD
+ for(; i < 8; i++)
+ {
+ float u = grbuf[18 + i];
+ float d = grbuf[17 - i];
+ grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];
+ grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];
+ }
+#endif
+ }
+}
+
+static void drmp3_L3_dct3_9(float *y)
+{
+ float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;
+
+ s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];
+ t0 = s0 + s6*0.5f;
+ s0 -= s6;
+ t4 = (s4 + s2)*0.93969262f;
+ t2 = (s8 + s2)*0.76604444f;
+ s6 = (s4 - s8)*0.17364818f;
+ s4 += s8 - s2;
+
+ s2 = s0 - s4*0.5f;
+ y[4] = s4 + s0;
+ s8 = t0 - t2 + s6;
+ s0 = t0 - t4 + t2;
+ s4 = t0 + t4 - s6;
+
+ s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];
+
+ s3 *= 0.86602540f;
+ t0 = (s5 + s1)*0.98480775f;
+ t4 = (s5 - s7)*0.34202014f;
+ t2 = (s1 + s7)*0.64278761f;
+ s1 = (s1 - s5 - s7)*0.86602540f;
+
+ s5 = t0 - s3 - t2;
+ s7 = t4 - s3 - t0;
+ s3 = t4 + s3 - t2;
+
+ y[0] = s4 - s7;
+ y[1] = s2 + s1;
+ y[2] = s0 - s3;
+ y[3] = s8 + s5;
+ y[5] = s8 - s5;
+ y[6] = s0 + s3;
+ y[7] = s2 - s1;
+ y[8] = s4 + s7;
+}
+
+static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)
+{
+ int i, j;
+ static const float g_twid9[18] = {
+ 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f
+ };
+
+ for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)
+ {
+ float co[9], si[9];
+ co[0] = -grbuf[0];
+ si[0] = grbuf[17];
+ for (i = 0; i < 4; i++)
+ {
+ si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2];
+ co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2];
+ si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3];
+ co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);
+ }
+ drmp3_L3_dct3_9(co);
+ drmp3_L3_dct3_9(si);
+
+ si[1] = -si[1];
+ si[3] = -si[3];
+ si[5] = -si[5];
+ si[7] = -si[7];
+
+ i = 0;
+
+#if DRMP3_HAVE_SIMD
+ if (drmp3_have_simd()) for (; i < 8; i += 4)
+ {
+ drmp3_f4 vovl = DRMP3_VLD(overlap + i);
+ drmp3_f4 vc = DRMP3_VLD(co + i);
+ drmp3_f4 vs = DRMP3_VLD(si + i);
+ drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i);
+ drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i);
+ drmp3_f4 vw0 = DRMP3_VLD(window + i);
+ drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i);
+ drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0));
+ DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1)));
+ DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1)));
+ vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0));
+ DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum));
+ }
+#endif
+ for (; i < 9; i++)
+ {
+ float ovl = overlap[i];
+ float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];
+ overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];
+ grbuf[i] = ovl*window[0 + i] - sum*window[9 + i];
+ grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];
+ }
+ }
+}
+
+static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst)
+{
+ float m1 = x1*0.86602540f;
+ float a1 = x0 - x2*0.5f;
+ dst[1] = x0 + x2;
+ dst[0] = a1 + m1;
+ dst[2] = a1 - m1;
+}
+
+static void drmp3_L3_imdct12(float *x, float *dst, float *overlap)
+{
+ static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };
+ float co[3], si[3];
+ int i;
+
+ drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);
+ drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);
+ si[1] = -si[1];
+
+ for (i = 0; i < 3; i++)
+ {
+ float ovl = overlap[i];
+ float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];
+ overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];
+ dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];
+ dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];
+ }
+}
+
+static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
+{
+ for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
+ {
+ float tmp[18];
+ memcpy(tmp, grbuf, sizeof(tmp));
+ memcpy(grbuf, overlap, 6*sizeof(float));
+ drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
+ drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
+ drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
+ }
+}
+
+static void drmp3_L3_change_sign(float *grbuf)
+{
+ int b, i;
+ for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)
+ for (i = 1; i < 18; i += 2)
+ grbuf[i] = -grbuf[i];
+}
+
+static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)
+{
+ static const float g_mdct_window[2][18] = {
+ { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f },
+ { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }
+ };
+ if (n_long_bands)
+ {
+ drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);
+ grbuf += 18*n_long_bands;
+ overlap += 9*n_long_bands;
+ }
+ if (block_type == DRMP3_SHORT_BLOCK_TYPE)
+ drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);
+ else
+ drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands);
+}
+
+static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s)
+{
+ int pos = (s->bs.pos + 7)/8u;
+ int remains = s->bs.limit/8u - pos;
+ if (remains > DRMP3_MAX_BITRESERVOIR_BYTES)
+ {
+ pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES;
+ remains = DRMP3_MAX_BITRESERVOIR_BYTES;
+ }
+ if (remains > 0)
+ {
+ memmove(h->reserv_buf, s->maindata + pos, remains);
+ }
+ h->reserv = remains;
+}
+
+static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin)
+{
+ int frame_bytes = (bs->limit - bs->pos)/8;
+ int bytes_have = DRMP3_MIN(h->reserv, main_data_begin);
+ memcpy(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin));
+ memcpy(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
+ drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
+ return h->reserv >= main_data_begin;
+}
+
+static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch)
+{
+ int ch;
+
+ for (ch = 0; ch < nch; ch++)
+ {
+ int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;
+ drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);
+ drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);
+ }
+
+ if (DRMP3_HDR_TEST_I_STEREO(h->header))
+ {
+ drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);
+ } else if (DRMP3_HDR_IS_MS_STEREO(h->header))
+ {
+ drmp3_L3_midside_stereo(s->grbuf[0], 576);
+ }
+
+ for (ch = 0; ch < nch; ch++, gr_info++)
+ {
+ int aa_bands = 31;
+ int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);
+
+ if (gr_info->n_short_sfb)
+ {
+ aa_bands = n_long_bands - 1;
+ drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);
+ }
+
+ drmp3_L3_antialias(s->grbuf[ch], aa_bands);
+ drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);
+ drmp3_L3_change_sign(s->grbuf[ch]);
+ }
+}
+
+static void drmp3d_DCT_II(float *grbuf, int n)
+{
+ static const float g_sec[24] = {
+ 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f
+ };
+ int i, k = 0;
+#if DRMP3_HAVE_SIMD
+ if (drmp3_have_simd()) for (; k < n; k += 4)
+ {
+ drmp3_f4 t[4][8], *x;
+ float *y = grbuf + k;
+
+ for (x = t[0], i = 0; i < 8; i++, x++)
+ {
+ drmp3_f4 x0 = DRMP3_VLD(&y[i*18]);
+ drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]);
+ drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]);
+ drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]);
+ drmp3_f4 t0 = DRMP3_VADD(x0, x3);
+ drmp3_f4 t1 = DRMP3_VADD(x1, x2);
+ drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]);
+ drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]);
+ x[0] = DRMP3_VADD(t0, t1);
+ x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]);
+ x[16] = DRMP3_VADD(t3, t2);
+ x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]);
+ }
+ for (x = t[0], i = 0; i < 4; i++, x += 8)
+ {
+ drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;
+ xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7);
+ x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6);
+ x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5);
+ x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4);
+ x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3);
+ x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2);
+ x[0] = DRMP3_VADD(x0, x1);
+ x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f);
+ x5 = DRMP3_VADD(x5, x6);
+ x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f);
+ x7 = DRMP3_VADD(x7, xt);
+ x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f);
+ x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */
+ x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f));
+ x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f));
+ x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6);
+ x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f);
+ x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f);
+ x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f);
+ x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f);
+ x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f);
+ x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f);
+ }
+
+ if (k > n - 3)
+ {
+#if DRMP3_HAVE_SSE
+#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
+#else
+#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v))
+#endif
+ for (i = 0; i < 7; i++, y += 4*18)
+ {
+ drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
+ DRMP3_VSAVE2(0, t[0][i]);
+ DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s));
+ DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
+ DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s));
+ }
+ DRMP3_VSAVE2(0, t[0][7]);
+ DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7]));
+ DRMP3_VSAVE2(2, t[1][7]);
+ DRMP3_VSAVE2(3, t[3][7]);
+ } else
+ {
+#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v)
+ for (i = 0; i < 7; i++, y += 4*18)
+ {
+ drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
+ DRMP3_VSAVE4(0, t[0][i]);
+ DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s));
+ DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1]));
+ DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s));
+ }
+ DRMP3_VSAVE4(0, t[0][7]);
+ DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7]));
+ DRMP3_VSAVE4(2, t[1][7]);
+ DRMP3_VSAVE4(3, t[3][7]);
+ }
+ } else
+#endif
+#ifdef DR_MP3_ONLY_SIMD
+ {}
+#else
+ for (; k < n; k++)
+ {
+ float t[4][8], *x, *y = grbuf + k;
+
+ for (x = t[0], i = 0; i < 8; i++, x++)
+ {
+ float x0 = y[i*18];
+ float x1 = y[(15 - i)*18];
+ float x2 = y[(16 + i)*18];
+ float x3 = y[(31 - i)*18];
+ float t0 = x0 + x3;
+ float t1 = x1 + x2;
+ float t2 = (x1 - x2)*g_sec[3*i + 0];
+ float t3 = (x0 - x3)*g_sec[3*i + 1];
+ x[0] = t0 + t1;
+ x[8] = (t0 - t1)*g_sec[3*i + 2];
+ x[16] = t3 + t2;
+ x[24] = (t3 - t2)*g_sec[3*i + 2];
+ }
+ for (x = t[0], i = 0; i < 4; i++, x += 8)
+ {
+ float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;
+ xt = x0 - x7; x0 += x7;
+ x7 = x1 - x6; x1 += x6;
+ x6 = x2 - x5; x2 += x5;
+ x5 = x3 - x4; x3 += x4;
+ x4 = x0 - x3; x0 += x3;
+ x3 = x1 - x2; x1 += x2;
+ x[0] = x0 + x1;
+ x[4] = (x0 - x1)*0.70710677f;
+ x5 = x5 + x6;
+ x6 = (x6 + x7)*0.70710677f;
+ x7 = x7 + xt;
+ x3 = (x3 + x4)*0.70710677f;
+ x5 -= x7*0.198912367f; /* rotate by PI/8 */
+ x7 += x5*0.382683432f;
+ x5 -= x7*0.198912367f;
+ x0 = xt - x6; xt += x6;
+ x[1] = (xt + x7)*0.50979561f;
+ x[2] = (x4 + x3)*0.54119611f;
+ x[3] = (x0 - x5)*0.60134488f;
+ x[5] = (x0 + x5)*0.89997619f;
+ x[6] = (x4 - x3)*1.30656302f;
+ x[7] = (xt - x7)*2.56291556f;
+
+ }
+ for (i = 0; i < 7; i++, y += 4*18)
+ {
+ y[0*18] = t[0][i];
+ y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
+ y[2*18] = t[1][i] + t[1][i + 1];
+ y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
+ }
+ y[0*18] = t[0][7];
+ y[1*18] = t[2][7] + t[3][7];
+ y[2*18] = t[1][7];
+ y[3*18] = t[3][7];
+ }
+#endif
+}
+
+#ifndef DR_MP3_FLOAT_OUTPUT
+typedef drmp3_int16 drmp3d_sample_t;
+
+static drmp3_int16 drmp3d_scale_pcm(float sample)
+{
+ if (sample >= 32766.5) return (drmp3_int16) 32767;
+ if (sample <= -32767.5) return (drmp3_int16)-32768;
+ drmp3_int16 s = (drmp3_int16)(sample + .5f);
+ s -= (s < 0); /* away from zero, to be compliant */
+ return (drmp3_int16)s;
+}
+#else
+typedef float drmp3d_sample_t;
+
+static float drmp3d_scale_pcm(float sample)
+{
+ return sample*(1.f/32768.f);
+}
+#endif
+
+static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z)
+{
+ float a;
+ a = (z[14*64] - z[ 0]) * 29;
+ a += (z[ 1*64] + z[13*64]) * 213;
+ a += (z[12*64] - z[ 2*64]) * 459;
+ a += (z[ 3*64] + z[11*64]) * 2037;
+ a += (z[10*64] - z[ 4*64]) * 5153;
+ a += (z[ 5*64] + z[ 9*64]) * 6574;
+ a += (z[ 8*64] - z[ 6*64]) * 37489;
+ a += z[ 7*64] * 75038;
+ pcm[0] = drmp3d_scale_pcm(a);
+
+ z += 2;
+ a = z[14*64] * 104;
+ a += z[12*64] * 1567;
+ a += z[10*64] * 9727;
+ a += z[ 8*64] * 64019;
+ a += z[ 6*64] * -9975;
+ a += z[ 4*64] * -45;
+ a += z[ 2*64] * 146;
+ a += z[ 0*64] * -5;
+ pcm[16*nch] = drmp3d_scale_pcm(a);
+}
+
+static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
+{
+ int i;
+ float *xr = xl + 576*(nch - 1);
+ drmp3d_sample_t *dstr = dstl + (nch - 1);
+
+ static const float g_win[] = {
+ -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
+ -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
+ -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
+ -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
+ -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
+ -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
+ -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
+ -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
+ -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
+ -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
+ -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
+ -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
+ -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
+ -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
+ -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
+ };
+ float *zlin = lins + 15*64;
+ const float *w = g_win;
+
+ zlin[4*15] = xl[18*16];
+ zlin[4*15 + 1] = xr[18*16];
+ zlin[4*15 + 2] = xl[0];
+ zlin[4*15 + 3] = xr[0];
+
+ zlin[4*31] = xl[1 + 18*16];
+ zlin[4*31 + 1] = xr[1 + 18*16];
+ zlin[4*31 + 2] = xl[1];
+ zlin[4*31 + 3] = xr[1];
+
+ drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
+ drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
+ drmp3d_synth_pair(dstl, nch, lins + 4*15);
+ drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
+
+#if DRMP3_HAVE_SIMD
+ if (drmp3_have_simd()) for (i = 14; i >= 0; i--)
+ {
+#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]);
+#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); }
+#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); }
+#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); }
+ drmp3_f4 a, b;
+ zlin[4*i] = xl[18*(31 - i)];
+ zlin[4*i + 1] = xr[18*(31 - i)];
+ zlin[4*i + 2] = xl[1 + 18*(31 - i)];
+ zlin[4*i + 3] = xr[1 + 18*(31 - i)];
+ zlin[4*i + 64] = xl[1 + 18*(1 + i)];
+ zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
+ zlin[4*i - 64 + 2] = xl[18*(1 + i)];
+ zlin[4*i - 64 + 3] = xr[18*(1 + i)];
+
+ DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7)
+
+ {
+#ifndef DR_MP3_FLOAT_OUTPUT
+#if DRMP3_HAVE_SSE
+ static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
+ static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
+ __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
+ _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
+ dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
+ dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
+ dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
+ dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
+ dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
+ dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
+ dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
+ dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
+#else
+ int16x4_t pcma, pcmb;
+ a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
+ b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
+ pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
+ pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
+ vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
+ vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
+ vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
+ vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
+ vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
+ vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
+ vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
+ vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
+#endif
+#else
+ static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
+ a = DRMP3_VMUL(a, g_scale);
+ b = DRMP3_VMUL(b, g_scale);
+#if DRMP3_HAVE_SSE
+ _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
+ _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
+ _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
+ _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
+ _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
+ _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
+ _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
+ _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
+#else
+ vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
+ vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
+ vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
+ vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
+ vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
+ vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
+ vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
+ vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
+#endif
+#endif /* DR_MP3_FLOAT_OUTPUT */
+ }
+ } else
+#endif
+#ifdef DR_MP3_ONLY_SIMD
+ {}
+#else
+ for (i = 14; i >= 0; i--)
+ {
+#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64];
+#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; }
+#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; }
+#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; }
+ float a[4], b[4];
+
+ zlin[4*i] = xl[18*(31 - i)];
+ zlin[4*i + 1] = xr[18*(31 - i)];
+ zlin[4*i + 2] = xl[1 + 18*(31 - i)];
+ zlin[4*i + 3] = xr[1 + 18*(31 - i)];
+ zlin[4*(i + 16)] = xl[1 + 18*(1 + i)];
+ zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];
+ zlin[4*(i - 16) + 2] = xl[18*(1 + i)];
+ zlin[4*(i - 16) + 3] = xr[18*(1 + i)];
+
+ DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7)
+
+ dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]);
+ dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]);
+ dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]);
+ dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]);
+ dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]);
+ dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]);
+ dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]);
+ dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]);
+ }
+#endif
+}
+
+static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins)
+{
+ int i;
+ for (i = 0; i < nch; i++)
+ {
+ drmp3d_DCT_II(grbuf + 576*i, nbands);
+ }
+
+ memcpy(lins, qmf_state, sizeof(float)*15*64);
+
+ for (i = 0; i < nbands; i += 2)
+ {
+ drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);
+ }
+#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL
+ if (nch == 1)
+ {
+ for (i = 0; i < 15*64; i += 2)
+ {
+ qmf_state[i] = lins[nbands*64 + i];
+ }
+ } else
+#endif
+ {
+ memcpy(qmf_state, lins + nbands*64, sizeof(float)*15*64);
+ }
+}
+
+static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes)
+{
+ int i, nmatch;
+ for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++)
+ {
+ i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i);
+ if (i + DRMP3_HDR_SIZE > mp3_bytes)
+ return nmatch > 0;
+ if (!drmp3_hdr_compare(hdr, hdr + i))
+ return 0;
+ }
+ return 1;
+}
+
+static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)
+{
+ int i, k;
+ for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++)
+ {
+ if (drmp3_hdr_valid(mp3))
+ {
+ int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes);
+ int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3);
+
+ for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++)
+ {
+ if (drmp3_hdr_compare(mp3, mp3 + k))
+ {
+ int fb = k - drmp3_hdr_padding(mp3);
+ int nextfb = fb + drmp3_hdr_padding(mp3 + k);
+ if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb))
+ continue;
+ frame_and_padding = k;
+ frame_bytes = fb;
+ *free_format_bytes = fb;
+ }
+ }
+
+ if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&
+ drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||
+ (!i && frame_and_padding == mp3_bytes))
+ {
+ *ptr_frame_bytes = frame_and_padding;
+ return i;
+ }
+ *free_format_bytes = 0;
+ }
+ }
+ *ptr_frame_bytes = 0;
+ return i;
+}
+
+void drmp3dec_init(drmp3dec *dec)
+{
+ dec->header[0] = 0;
+}
+
+int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
+{
+ int i = 0, igr, frame_size = 0, success = 1;
+ const drmp3_uint8 *hdr;
+ drmp3_bs bs_frame[1];
+ drmp3dec_scratch scratch;
+
+ if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3))
+ {
+ frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3);
+ if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size)))
+ {
+ frame_size = 0;
+ }
+ }
+ if (!frame_size)
+ {
+ memset(dec, 0, sizeof(drmp3dec));
+ i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
+ if (!frame_size || i + frame_size > mp3_bytes)
+ {
+ info->frame_bytes = i;
+ return 0;
+ }
+ }
+
+ hdr = mp3 + i;
+ memcpy(dec->header, hdr, DRMP3_HDR_SIZE);
+ info->frame_bytes = i + frame_size;
+ info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
+ info->hz = drmp3_hdr_sample_rate_hz(hdr);
+ info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
+ info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
+
+ if (!pcm)
+ {
+ return drmp3_hdr_frame_samples(hdr);
+ }
+
+ drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE);
+ if (DRMP3_HDR_IS_CRC(hdr))
+ {
+ drmp3_bs_get_bits(bs_frame, 16);
+ }
+
+ if (info->layer == 3)
+ {
+ int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);
+ if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
+ {
+ drmp3dec_init(dec);
+ return 0;
+ }
+ success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
+ if (success)
+ {
+ for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
+ {
+ memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
+ drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
+ drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
+ }
+ }
+ drmp3_L3_save_reservoir(dec, &scratch);
+ } else
+ {
+#ifdef DR_MP3_ONLY_MP3
+ return 0;
+#else
+ drmp3_L12_scale_info sci[1];
+ drmp3_L12_read_scale_info(hdr, bs_frame, sci);
+
+ memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
+ for (i = 0, igr = 0; igr < 3; igr++)
+ {
+ if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
+ {
+ i = 0;
+ drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
+ drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
+ memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
+ pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
+ }
+ if (bs_frame->pos > bs_frame->limit)
+ {
+ drmp3dec_init(dec);
+ return 0;
+ }
+ }
+#endif
+ }
+ return success*drmp3_hdr_frame_samples(dec->header);
+}
+
+void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
+{
+ if(num_samples > 0)
+ {
+ int i = 0;
+#if DRMP3_HAVE_SIMD
+ int aligned_count = num_samples & ~7;
+ for(; i < aligned_count; i+=8)
+ {
+ static const drmp3_f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f };
+ drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), g_scale);
+ drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), g_scale);
+#if DRMP3_HAVE_SSE
+ static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
+ static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
+ __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
+ _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
+ out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
+ out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
+ out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
+ out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
+ out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
+ out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
+ out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
+ out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
+#else
+ int16x4_t pcma, pcmb;
+ a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
+ b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
+ pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
+ pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
+ vst1_lane_s16(out+i , pcma, 0);
+ vst1_lane_s16(out+i+1, pcma, 1);
+ vst1_lane_s16(out+i+2, pcma, 2);
+ vst1_lane_s16(out+i+3, pcma, 3);
+ vst1_lane_s16(out+i+4, pcmb, 0);
+ vst1_lane_s16(out+i+5, pcmb, 1);
+ vst1_lane_s16(out+i+6, pcmb, 2);
+ vst1_lane_s16(out+i+7, pcmb, 3);
+#endif
+ }
+#endif
+ for(; i < num_samples; i++)
+ {
+ float sample = in[i] * 32768.0f;
+ if (sample >= 32766.5)
+ out[i] = (drmp3_int16) 32767;
+ else if (sample <= -32767.5)
+ out[i] = (drmp3_int16)-32768;
+ else
+ {
+ short s = (drmp3_int16)(sample + .5f);
+ s -= (s < 0); /* away from zero, to be compliant */
+ out[i] = s;
+ }
+ }
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Main Public API
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(SIZE_MAX)
+ #define DRMP3_SIZE_MAX SIZE_MAX
+#else
+ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+ #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRMP3_SIZE_MAX 0xFFFFFFFF
+ #endif
+#endif
+
+// Options.
+#ifndef DR_MP3_DEFAULT_CHANNELS
+#define DR_MP3_DEFAULT_CHANNELS 2
+#endif
+#ifndef DR_MP3_DEFAULT_SAMPLE_RATE
+#define DR_MP3_DEFAULT_SAMPLE_RATE 44100
+#endif
+
+
+// Standard library stuff.
+#ifndef DRMP3_ASSERT
+#include
+#define DRMP3_ASSERT(expression) assert(expression)
+#endif
+#ifndef DRMP3_COPY_MEMORY
+#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
+#endif
+#ifndef DRMP3_ZERO_MEMORY
+#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
+#endif
+#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p)))
+#ifndef DRMP3_MALLOC
+#define DRMP3_MALLOC(sz) malloc((sz))
+#endif
+#ifndef DRMP3_REALLOC
+#define DRMP3_REALLOC(p, sz) realloc((p), (sz))
+#endif
+#ifndef DRMP3_FREE
+#define DRMP3_FREE(p) free((p))
+#endif
+
+#define drmp3_assert DRMP3_ASSERT
+#define drmp3_copy_memory DRMP3_COPY_MEMORY
+#define drmp3_zero_memory DRMP3_ZERO_MEMORY
+#define drmp3_zero_object DRMP3_ZERO_OBJECT
+#define drmp3_malloc DRMP3_MALLOC
+#define drmp3_realloc DRMP3_REALLOC
+
+#define drmp3_countof(x) (sizeof(x) / sizeof(x[0]))
+#define drmp3_max(x, y) (((x) > (y)) ? (x) : (y))
+#define drmp3_min(x, y) (((x) < (y)) ? (x) : (y))
+
+#define DRMP3_DATA_CHUNK_SIZE 16384 // The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends 16K.
+
+static inline float drmp3_mix_f32(float x, float y, float a)
+{
+ return x*(1-a) + y*a;
+}
+
+static void drmp3_blend_f32(float* pOut, float* pInA, float* pInB, float factor, drmp3_uint32 channels)
+{
+ for (drmp3_uint32 i = 0; i < channels; ++i) {
+ pOut[i] = drmp3_mix_f32(pInA[i], pInB[i], factor);
+ }
+}
+
+void drmp3_src_cache_init(drmp3_src* pSRC, drmp3_src_cache* pCache)
+{
+ drmp3_assert(pSRC != NULL);
+ drmp3_assert(pCache != NULL);
+
+ pCache->pSRC = pSRC;
+ pCache->cachedFrameCount = 0;
+ pCache->iNextFrame = 0;
+}
+
+drmp3_uint64 drmp3_src_cache_read_frames(drmp3_src_cache* pCache, drmp3_uint64 frameCount, float* pFramesOut)
+{
+ drmp3_assert(pCache != NULL);
+ drmp3_assert(pCache->pSRC != NULL);
+ drmp3_assert(pCache->pSRC->onRead != NULL);
+ drmp3_assert(frameCount > 0);
+ drmp3_assert(pFramesOut != NULL);
+
+ drmp3_uint32 channels = pCache->pSRC->config.channels;
+
+ drmp3_uint64 totalFramesRead = 0;
+ while (frameCount > 0) {
+ // If there's anything in memory go ahead and copy that over first.
+ drmp3_uint64 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame;
+ drmp3_uint64 framesToReadFromMemory = frameCount;
+ if (framesToReadFromMemory > framesRemainingInMemory) {
+ framesToReadFromMemory = framesRemainingInMemory;
+ }
+
+ drmp3_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, (drmp3_uint32)(framesToReadFromMemory * channels * sizeof(float)));
+ pCache->iNextFrame += (drmp3_uint32)framesToReadFromMemory;
+
+ totalFramesRead += framesToReadFromMemory;
+ frameCount -= framesToReadFromMemory;
+ if (frameCount == 0) {
+ break;
+ }
+
+
+ // At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data.
+ drmp3_assert(frameCount > 0);
+ pFramesOut += framesToReadFromMemory * channels;
+
+ pCache->iNextFrame = 0;
+ pCache->cachedFrameCount = 0;
+
+ drmp3_uint32 framesToReadFromClient = drmp3_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels;
+ if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) {
+ framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames;
+ }
+
+ pCache->cachedFrameCount = (drmp3_uint32)pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData);
+
+
+ // Get out of this loop if nothing was able to be retrieved.
+ if (pCache->cachedFrameCount == 0) {
+ break;
+ }
+ }
+
+ return totalFramesRead;
+}
+
+
+drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush);
+drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush);
+
+drmp3_bool32 drmp3_src_init(const drmp3_src_config* pConfig, drmp3_src_read_proc onRead, void* pUserData, drmp3_src* pSRC)
+{
+ if (pSRC == NULL) return DRMP3_FALSE;
+ drmp3_zero_object(pSRC);
+
+ if (pConfig == NULL || onRead == NULL) return DRMP3_FALSE;
+ if (pConfig->channels == 0 || pConfig->channels > 2) return DRMP3_FALSE;
+
+ pSRC->config = *pConfig;
+ pSRC->onRead = onRead;
+ pSRC->pUserData = pUserData;
+
+ if (pSRC->config.cacheSizeInFrames > DRMP3_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) {
+ pSRC->config.cacheSizeInFrames = DRMP3_SRC_CACHE_SIZE_IN_FRAMES;
+ }
+
+ drmp3_src_cache_init(pSRC, &pSRC->cache);
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_src_set_input_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateIn)
+{
+ if (pSRC == NULL) return DRMP3_FALSE;
+
+ // Must have a sample rate of > 0.
+ if (sampleRateIn == 0) {
+ return DRMP3_FALSE;
+ }
+
+ pSRC->config.sampleRateIn = sampleRateIn;
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_src_set_output_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateOut)
+{
+ if (pSRC == NULL) return DRMP3_FALSE;
+
+ // Must have a sample rate of > 0.
+ if (sampleRateOut == 0) {
+ return DRMP3_FALSE;
+ }
+
+ pSRC->config.sampleRateOut = sampleRateOut;
+ return DRMP3_TRUE;
+}
+
+drmp3_uint64 drmp3_src_read_frames_ex(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush)
+{
+ if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0;
+
+ drmp3_src_algorithm algorithm = pSRC->config.algorithm;
+
+ // Always use passthrough if the sample rates are the same.
+ if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) {
+ algorithm = drmp3_src_algorithm_none;
+ }
+
+ // Could just use a function pointer instead of a switch for this...
+ switch (algorithm)
+ {
+ case drmp3_src_algorithm_none: return drmp3_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush);
+ case drmp3_src_algorithm_linear: return drmp3_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush);
+ default: return 0;
+ }
+}
+
+drmp3_uint64 drmp3_src_read_frames(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut)
+{
+ return drmp3_src_read_frames_ex(pSRC, frameCount, pFramesOut, DRMP3_FALSE);
+}
+
+drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush)
+{
+ drmp3_assert(pSRC != NULL);
+ drmp3_assert(frameCount > 0);
+ drmp3_assert(pFramesOut != NULL);
+
+ (void)flush; // Passthrough need not care about flushing.
+ return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData);
+}
+
+drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush)
+{
+ drmp3_assert(pSRC != NULL);
+ drmp3_assert(frameCount > 0);
+ drmp3_assert(pFramesOut != NULL);
+
+ // For linear SRC, the bin is only 2 frames: 1 prior, 1 future.
+
+ // Load the bin if necessary.
+ if (!pSRC->algo.linear.isPrevFramesLoaded) {
+ drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin);
+ if (framesRead == 0) {
+ return 0;
+ }
+ pSRC->algo.linear.isPrevFramesLoaded = DRMP3_TRUE;
+ }
+ if (!pSRC->algo.linear.isNextFramesLoaded) {
+ drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels);
+ if (framesRead == 0) {
+ return 0;
+ }
+ pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE;
+ }
+
+ float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
+
+ drmp3_uint64 totalFramesRead = 0;
+ while (frameCount > 0) {
+ // The bin is where the previous and next frames are located.
+ float* pPrevFrame = pSRC->bin;
+ float* pNextFrame = pSRC->bin + pSRC->config.channels;
+
+ drmp3_blend_f32((float*)pFramesOut, pPrevFrame, pNextFrame, pSRC->algo.linear.alpha, pSRC->config.channels);
+
+ pSRC->algo.linear.alpha += factor;
+
+ // The new alpha value is how we determine whether or not we need to read fresh frames.
+ drmp3_uint32 framesToReadFromClient = (drmp3_uint32)pSRC->algo.linear.alpha;
+ pSRC->algo.linear.alpha = pSRC->algo.linear.alpha - framesToReadFromClient;
+
+ for (drmp3_uint32 i = 0; i < framesToReadFromClient; ++i) {
+ for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) {
+ pPrevFrame[j] = pNextFrame[j];
+ }
+
+ drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame);
+ if (framesRead == 0) {
+ for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) {
+ pNextFrame[j] = 0;
+ }
+
+ if (pSRC->algo.linear.isNextFramesLoaded) {
+ pSRC->algo.linear.isNextFramesLoaded = DRMP3_FALSE;
+ } else {
+ if (flush) {
+ pSRC->algo.linear.isPrevFramesLoaded = DRMP3_FALSE;
+ }
+ }
+
+ break;
+ }
+ }
+
+ pFramesOut = (drmp3_uint8*)pFramesOut + (1 * pSRC->config.channels * sizeof(float));
+ frameCount -= 1;
+ totalFramesRead += 1;
+
+ // If there's no frames available we need to get out of this loop.
+ if (!pSRC->algo.linear.isNextFramesLoaded && (!flush || !pSRC->algo.linear.isPrevFramesLoaded)) {
+ break;
+ }
+ }
+
+ return totalFramesRead;
+}
+
+
+static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
+{
+ return pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
+}
+
+static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
+{
+ drmp3_assert(offset >= 0);
+ return pMP3->onSeek(pMP3->pUserData, offset, origin);
+}
+
+
+static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
+{
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(pMP3->onRead != NULL);
+
+ if (pMP3->atEnd) {
+ return 0;
+ }
+
+ drmp3_uint32 pcmFramesRead = 0;
+ do {
+ // minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more.
+ if (pMP3->dataSize < DRMP3_DATA_CHUNK_SIZE) {
+ if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
+ pMP3->dataCapacity = DRMP3_DATA_CHUNK_SIZE;
+ drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity);
+ if (pNewData == NULL) {
+ return 0; // Out of memory.
+ }
+
+ pMP3->pData = pNewData;
+ }
+
+ size_t bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
+ if (bytesRead == 0) {
+ if (pMP3->dataSize == 0) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; // No data.
+ }
+ }
+
+ pMP3->dataSize += bytesRead;
+ }
+
+ if (pMP3->dataSize > INT_MAX) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; // File too big.
+ }
+
+ drmp3dec_frame_info info;
+ pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pPCMFrames, &info); // <-- Safe size_t -> int conversion thanks to the check above.
+ if (pcmFramesRead != 0) {
+ size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes);
+ for (size_t i = 0; i < leftoverDataSize; ++i) {
+ pMP3->pData[i] = pMP3->pData[i + (size_t)info.frame_bytes];
+ }
+
+ pMP3->dataSize = leftoverDataSize;
+ pMP3->pcmFramesConsumedInMP3Frame = 0;
+ pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
+ pMP3->mp3FrameChannels = info.channels;
+ pMP3->mp3FrameSampleRate = info.hz;
+ drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->mp3FrameSampleRate);
+ break;
+ } else {
+ // Need more data. minimp3 recommends doing data submission in 16K chunks.
+ if (pMP3->dataCapacity == pMP3->dataSize) {
+ // No room. Expand.
+ pMP3->dataCapacity += DRMP3_DATA_CHUNK_SIZE;
+ drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity);
+ if (pNewData == NULL) {
+ return 0; // Out of memory.
+ }
+
+ pMP3->pData = pNewData;
+ }
+
+ // Fill in a chunk.
+ size_t bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
+ if (bytesRead == 0) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; // Error reading more data.
+ }
+
+ pMP3->dataSize += bytesRead;
+ }
+ } while (DRMP3_TRUE);
+
+ return pcmFramesRead;
+}
+
+static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
+{
+ drmp3_assert(pMP3 != NULL);
+ return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames);
+}
+
+static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
+{
+ drmp3_assert(pMP3 != NULL);
+
+ drmp3_uint32 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
+ if (pcmFrameCount == 0) {
+ return 0;
+ }
+
+ // We have essentially just skipped past the frame, so just set the remaining samples to 0.
+ pMP3->currentPCMFrame += pcmFrameCount;
+ pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
+ pMP3->pcmFramesRemainingInMP3Frame = 0;
+
+ return pcmFrameCount;
+}
+
+static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData)
+{
+ drmp3* pMP3 = (drmp3*)pUserData;
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(pMP3->onRead != NULL);
+
+ float* pFramesOutF = (float*)pFramesOut;
+ drmp3_uint64 totalFramesRead = 0;
+
+ while (frameCount > 0) {
+ // Read from the in-memory buffer first.
+ while (pMP3->pcmFramesRemainingInMP3Frame > 0 && frameCount > 0) {
+ drmp3d_sample_t* frames = (drmp3d_sample_t*)pMP3->pcmFrames;
+#ifndef DR_MP3_FLOAT_OUTPUT
+ if (pMP3->mp3FrameChannels == 1) {
+ if (pMP3->channels == 1) {
+ // Mono -> Mono.
+ pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
+ } else {
+ // Mono -> Stereo.
+ pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
+ pFramesOutF[1] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
+ }
+ } else {
+ if (pMP3->channels == 1) {
+ // Stereo -> Mono
+ float sample = 0;
+ sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0] / 32768.0f;
+ sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1] / 32768.0f;
+ pFramesOutF[0] = sample * 0.5f;
+ } else {
+ // Stereo -> Stereo
+ pFramesOutF[0] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0] / 32768.0f;
+ pFramesOutF[1] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1] / 32768.0f;
+ }
+ }
+#else
+ if (pMP3->mp3FrameChannels == 1) {
+ if (pMP3->channels == 1) {
+ // Mono -> Mono.
+ pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame];
+ } else {
+ // Mono -> Stereo.
+ pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame];
+ pFramesOutF[1] = frames[pMP3->pcmFramesConsumedInMP3Frame];
+ }
+ } else {
+ if (pMP3->channels == 1) {
+ // Stereo -> Mono
+ float sample = 0;
+ sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0];
+ sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1];
+ pFramesOutF[0] = sample * 0.5f;
+ } else {
+ // Stereo -> Stereo
+ pFramesOutF[0] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0];
+ pFramesOutF[1] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1];
+ }
+ }
+#endif
+
+ pMP3->pcmFramesConsumedInMP3Frame += 1;
+ pMP3->pcmFramesRemainingInMP3Frame -= 1;
+ totalFramesRead += 1;
+ frameCount -= 1;
+ pFramesOutF += pSRC->config.channels;
+ }
+
+ if (frameCount == 0) {
+ break;
+ }
+
+ drmp3_assert(pMP3->pcmFramesRemainingInMP3Frame == 0);
+
+ // At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed
+ // at this point which means we'll also need to update our sample rate conversion pipeline.
+ if (drmp3_decode_next_frame(pMP3) == 0) {
+ break;
+ }
+ }
+
+ return totalFramesRead;
+}
+
+drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig)
+{
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(onRead != NULL);
+
+ // This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break.
+ drmp3dec_init(&pMP3->decoder);
+
+ // The config can be null in which case we use defaults.
+ drmp3_config config;
+ if (pConfig != NULL) {
+ config = *pConfig;
+ } else {
+ drmp3_zero_object(&config);
+ }
+
+ pMP3->channels = config.outputChannels;
+ if (pMP3->channels == 0) {
+ pMP3->channels = DR_MP3_DEFAULT_CHANNELS;
+ }
+
+ // Cannot have more than 2 channels.
+ if (pMP3->channels > 2) {
+ pMP3->channels = 2;
+ }
+
+ pMP3->sampleRate = config.outputSampleRate;
+ if (pMP3->sampleRate == 0) {
+ pMP3->sampleRate = DR_MP3_DEFAULT_SAMPLE_RATE;
+ }
+
+ pMP3->onRead = onRead;
+ pMP3->onSeek = onSeek;
+ pMP3->pUserData = pUserData;
+
+ // We need a sample rate converter for converting the sample rate from the MP3 frames to the requested output sample rate.
+ drmp3_src_config srcConfig;
+ drmp3_zero_object(&srcConfig);
+ srcConfig.sampleRateIn = DR_MP3_DEFAULT_SAMPLE_RATE;
+ srcConfig.sampleRateOut = pMP3->sampleRate;
+ srcConfig.channels = pMP3->channels;
+ srcConfig.algorithm = drmp3_src_algorithm_linear;
+ if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) {
+ drmp3_uninit(pMP3);
+ return DRMP3_FALSE;
+ }
+
+ // Decode the first frame to confirm that it is indeed a valid MP3 stream.
+ if (!drmp3_decode_next_frame(pMP3)) {
+ drmp3_uninit(pMP3);
+ return DRMP3_FALSE; // Not a valid MP3 stream.
+ }
+
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig)
+{
+ if (pMP3 == NULL || onRead == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ drmp3_zero_object(pMP3);
+ return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pConfig);
+}
+
+
+static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ drmp3* pMP3 = (drmp3*)pUserData;
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
+
+ size_t bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
+ if (bytesToRead > bytesRemaining) {
+ bytesToRead = bytesRemaining;
+ }
+
+ if (bytesToRead > 0) {
+ drmp3_copy_memory(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
+ pMP3->memory.currentReadPos += bytesToRead;
+ }
+
+ return bytesToRead;
+}
+
+static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin)
+{
+ drmp3* pMP3 = (drmp3*)pUserData;
+ drmp3_assert(pMP3 != NULL);
+
+ if (origin == drmp3_seek_origin_current) {
+ if (byteOffset > 0) {
+ if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
+ byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); // Trying to seek too far forward.
+ }
+ } else {
+ if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
+ byteOffset = -(int)pMP3->memory.currentReadPos; // Trying to seek too far backwards.
+ }
+ }
+
+ // This will never underflow thanks to the clamps above.
+ pMP3->memory.currentReadPos += byteOffset;
+ } else {
+ if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) {
+ pMP3->memory.currentReadPos = byteOffset;
+ } else {
+ pMP3->memory.currentReadPos = pMP3->memory.dataSize; // Trying to seek too far forward.
+ }
+ }
+
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig)
+{
+ if (pMP3 == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ drmp3_zero_object(pMP3);
+
+ if (pData == NULL || dataSize == 0) {
+ return DRMP3_FALSE;
+ }
+
+ pMP3->memory.pData = (const drmp3_uint8*)pData;
+ pMP3->memory.dataSize = dataSize;
+ pMP3->memory.currentReadPos = 0;
+
+ return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pConfig);
+}
+
+
+#ifndef DR_MP3_NO_STDIO
+#include
+
+static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
+}
+
+static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
+{
+ return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig)
+{
+ FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (fopen_s(&pFile, filePath, "rb") != 0) {
+ return DRMP3_FALSE;
+ }
+#else
+ pFile = fopen(filePath, "rb");
+ if (pFile == NULL) {
+ return DRMP3_FALSE;
+ }
+#endif
+
+ return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pConfig);
+}
+#endif
+
+void drmp3_uninit(drmp3* pMP3)
+{
+ if (pMP3 == NULL) {
+ return;
+ }
+
+#ifndef DR_MP3_NO_STDIO
+ if (pMP3->onRead == drmp3__on_read_stdio) {
+ fclose((FILE*)pMP3->pUserData);
+ }
+#endif
+
+ drmp3_free(pMP3->pData);
+}
+
+drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
+{
+ if (pMP3 == NULL || pMP3->onRead == NULL) {
+ return 0;
+ }
+
+ drmp3_uint64 totalFramesRead = 0;
+
+ if (pBufferOut == NULL) {
+ float temp[4096];
+ while (framesToRead > 0) {
+ drmp3_uint64 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels;
+ if (framesToReadRightNow > framesToRead) {
+ framesToReadRightNow = framesToRead;
+ }
+
+ drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
+ if (framesJustRead == 0) {
+ break;
+ }
+
+ framesToRead -= framesJustRead;
+ totalFramesRead += framesJustRead;
+ }
+ } else {
+ totalFramesRead = drmp3_src_read_frames_ex(&pMP3->src, framesToRead, pBufferOut, DRMP3_TRUE);
+ pMP3->currentPCMFrame += totalFramesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
+{
+ drmp3_assert(pMP3 != NULL);
+ drmp3_assert(pMP3->onSeek != NULL);
+
+ // Seek to the start of the stream to begin with.
+ if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
+ return DRMP3_FALSE;
+ }
+
+ // Clear any cached data.
+ pMP3->pcmFramesConsumedInMP3Frame = 0;
+ pMP3->pcmFramesRemainingInMP3Frame = 0;
+ pMP3->currentPCMFrame = 0;
+ pMP3->dataSize = 0;
+ pMP3->atEnd = DRMP3_FALSE;
+
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
+{
+ drmp3_assert(pMP3 != NULL);
+
+ if (frameIndex == pMP3->currentPCMFrame) {
+ return DRMP3_TRUE;
+ }
+
+ // If we're moving foward we just read from where we're at. Otherwise we need to move back to the start of
+ // the stream and read from the beginning.
+ drmp3_uint64 framesToReadAndDiscard;
+ if (frameIndex >= pMP3->currentPCMFrame) {
+ // Moving foward.
+ framesToReadAndDiscard = frameIndex - pMP3->currentPCMFrame;
+ } else {
+ // Moving backward. Move to the start of the stream and then move forward.
+ framesToReadAndDiscard = frameIndex;
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return DRMP3_FALSE;
+ }
+ }
+
+ // MP3 is a bit annoying when it comes to seeking because of the bit reservoir. It basically means that an MP3 frame can possibly
+ // depend on some of the data of prior frames. This means it's not as simple as seeking to the first byte of the MP3 frame that
+ // contains the sample because that MP3 frame will need the data from the previous MP3 frame (which we just seeked past!). To
+ // resolve this we seek past a number of MP3 frames up to a point, and then read-and-discard the remainder.
+ drmp3_uint64 maxFramesToReadAndDiscard = DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME * 3;
+
+ // First get rid of anything that's still sitting in the buffer.
+ if (framesToReadAndDiscard > maxFramesToReadAndDiscard && framesToReadAndDiscard > pMP3->pcmFramesRemainingInMP3Frame) {
+ framesToReadAndDiscard -= pMP3->pcmFramesRemainingInMP3Frame;
+ pMP3->currentPCMFrame += pMP3->pcmFramesRemainingInMP3Frame;
+ pMP3->pcmFramesConsumedInMP3Frame += pMP3->pcmFramesRemainingInMP3Frame;
+ pMP3->pcmFramesRemainingInMP3Frame = 0;
+ }
+
+ // Now get rid of leading whole frames.
+ while (framesToReadAndDiscard > maxFramesToReadAndDiscard) {
+ drmp3_uint32 pcmFramesSeeked = drmp3_seek_next_frame(pMP3);
+ if (pcmFramesSeeked == 0) {
+ break;
+ }
+
+ framesToReadAndDiscard -= pcmFramesSeeked;
+ }
+
+ // The last step is to read-and-discard any remaining PCM frames to make it sample-exact.
+ drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadAndDiscard, NULL);
+ if (framesRead != framesToReadAndDiscard) {
+ return DRMP3_FALSE;
+ }
+
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
+{
+ if (pMP3 == NULL || pMP3->onSeek == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ // We currently only support brute force seeking.
+ return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
+}
+
+drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
+{
+ if (pMP3 == NULL) {
+ return 0;
+ }
+
+ // The way this works is we move back to the start of the stream, iterate over each MP3 frame and calculate the frame count based
+ // on our output sample rate, the seek back to the PCM frame we were sitting on before calling this function.
+
+ // The stream must support seeking for this to work.
+ if (pMP3->onSeek == NULL) {
+ return 0;
+ }
+
+ // We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later.
+ drmp3_uint64 currentPCMFrame = pMP3->currentPCMFrame;
+
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return 0;
+ }
+
+ drmp3_uint64 totalPCMFrameCount = 0;
+ float totalPCMFrameCountFractionalPart = 0; // <-- With resampling there will be a fractional part to each MP3 frame that we need to accumulate.
+ for (;;) {
+ drmp3_uint32 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); // <-- Passing in NULL here will prevent decoding of the MP3 frame which should save time.
+ if (pcmFramesInCurrentMP3FrameIn == 0) {
+ break;
+ }
+
+ float srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
+ drmp3_assert(srcRatio > 0);
+
+ float pcmFramesInCurrentMP3FrameOutF = totalPCMFrameCountFractionalPart + (pcmFramesInCurrentMP3FrameIn / srcRatio);
+ drmp3_uint32 pcmFramesInCurrentMP3FrameOut = (drmp3_uint32)pcmFramesInCurrentMP3FrameOutF;
+ totalPCMFrameCountFractionalPart = pcmFramesInCurrentMP3FrameOutF - pcmFramesInCurrentMP3FrameOut;
+ totalPCMFrameCount += pcmFramesInCurrentMP3FrameOut;
+ }
+
+ // Finally, we need to seek back to where we were.
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return 0;
+ }
+
+ if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
+ return 0;
+ }
+
+ return totalPCMFrameCount;
+}
+
+drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
+{
+ if (pMP3 == NULL) {
+ return 0;
+ }
+
+ // This works the same way as drmp3_get_pcm_frame_count() - move to the start, count MP3 frames, move back to the previous position.
+
+ // The stream must support seeking for this to work.
+ if (pMP3->onSeek == NULL) {
+ return 0;
+ }
+
+ // We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later.
+ drmp3_uint64 currentPCMFrame = pMP3->currentPCMFrame;
+
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return 0;
+ }
+
+ drmp3_uint64 totalMP3FrameCount = 0;
+ for (;;) {
+ drmp3_uint32 pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
+ if (pcmFramesInCurrentMP3FrameIn == 0) {
+ break;
+ }
+
+ totalMP3FrameCount += 1;
+ }
+
+ // Finally, we need to seek back to where we were.
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return 0;
+ }
+
+ if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
+ return 0;
+ }
+
+ return totalMP3FrameCount;
+}
+
+
+float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3_assert(pMP3 != NULL);
+
+ drmp3_uint64 totalFramesRead = 0;
+ drmp3_uint64 framesCapacity = 0;
+ float* pFrames = NULL;
+
+ float temp[4096];
+ for (;;) {
+ drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels;
+ drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
+ if (framesJustRead == 0) {
+ break;
+ }
+
+ // Reallocate the output buffer if there's not enough room.
+ if (framesCapacity < totalFramesRead + framesJustRead) {
+ framesCapacity *= 2;
+ if (framesCapacity < totalFramesRead + framesJustRead) {
+ framesCapacity = totalFramesRead + framesJustRead;
+ }
+
+ drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float);
+ if (newFramesBufferSize > DRMP3_SIZE_MAX) {
+ break;
+ }
+
+ float* pNewFrames = (float*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize);
+ if (pNewFrames == NULL) {
+ drmp3_free(pFrames);
+ break;
+ }
+
+ pFrames = pNewFrames;
+ }
+
+ drmp3_copy_memory(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
+ totalFramesRead += framesJustRead;
+
+ // If the number of frames we asked for is less that what we actually read it means we've reached the end.
+ if (framesJustRead != framesToReadRightNow) {
+ break;
+ }
+ }
+
+ if (pConfig != NULL) {
+ pConfig->outputChannels = pMP3->channels;
+ pConfig->outputSampleRate = pMP3->sampleRate;
+ }
+
+ drmp3_uninit(pMP3);
+
+ if (pTotalFrameCount) *pTotalFrameCount = totalFramesRead;
+ return pFrames;
+}
+
+float* drmp3_open_and_read_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3 mp3;
+ if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pConfig)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
+}
+
+float* drmp3_open_memory_and_read_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3 mp3;
+ if (!drmp3_init_memory(&mp3, pData, dataSize, pConfig)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
+}
+
+#ifndef DR_MP3_NO_STDIO
+float* drmp3_open_file_and_read_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3 mp3;
+ if (!drmp3_init_file(&mp3, filePath, pConfig)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
+}
+#endif
+
+void drmp3_free(void* p)
+{
+ DRMP3_FREE(p);
+}
+
+#endif /*DR_MP3_IMPLEMENTATION*/
+
+
+// DIFFERENCES BETWEEN minimp3 AND dr_mp3
+// ======================================
+// - First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was done. All of the
+// code relating to the actual decoding remains mostly unmodified, apart from some namespacing changes.
+// - dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather than pushing data
+// to the decoder, the decoder _pulls_ data from your callbacks.
+// - In addition to callbacks, a decoder can be initialized from a block of memory and a file.
+// - The dr_mp3 pull API reads PCM frames rather than whole MP3 frames.
+// - dr_mp3 adds convenience APIs for opening and decoding entire files in one go.
+// - dr_mp3 is fully namespaced, including the implementation section, which is more suitable when compiling projects
+// as a single translation unit (aka unity builds). At the time of writing this, a unity build is not possible when
+// using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this.
+
+
+// REVISION HISTORY
+// ================
+//
+// v0.4.0 - 2018-xx-xx
+// - API CHANGE: Rename some APIs:
+// - drmp3_read_f32 -> to drmp3_read_pcm_frames_f32
+// - drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame
+// - drmp3_open_and_decode_f32 -> drmp3_open_and_read_f32
+// - drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_f32
+// - drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_f32
+// - Add drmp3_get_pcm_frame_count().
+// - Add drmp3_get_mp3_frame_count().
+// - Improve seeking performance.
+//
+// v0.3.2 - 2018-09-11
+// - Fix a couple of memory leaks.
+// - Bring up to date with minimp3.
+//
+// v0.3.1 - 2018-08-25
+// - Fix C++ build.
+//
+// v0.3.0 - 2018-08-25
+// - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has
+// been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or
+// not the DR_MP3_FLOAT_OUTPUT option is set.
+//
+// v0.2.11 - 2018-08-08
+// - Fix a bug where the last part of a file is not read.
+//
+// v0.2.10 - 2018-08-07
+// - Improve 64-bit detection.
+//
+// v0.2.9 - 2018-08-05
+// - Fix C++ build on older versions of GCC.
+// - Bring up to date with minimp3.
+//
+// v0.2.8 - 2018-08-02
+// - Fix compilation errors with older versions of GCC.
+//
+// v0.2.7 - 2018-07-13
+// - Bring up to date with minimp3.
+//
+// v0.2.6 - 2018-07-12
+// - Bring up to date with minimp3.
+//
+// v0.2.5 - 2018-06-22
+// - Bring up to date with minimp3.
+//
+// v0.2.4 - 2018-05-12
+// - Bring up to date with minimp3.
+//
+// v0.2.3 - 2018-04-29
+// - Fix TCC build.
+//
+// v0.2.2 - 2018-04-28
+// - Fix bug when opening a decoder from memory.
+//
+// v0.2.1 - 2018-04-27
+// - Efficiency improvements when the decoder reaches the end of the stream.
+//
+// v0.2 - 2018-04-21
+// - Bring up to date with minimp3.
+// - Start using major.minor.revision versioning.
+//
+// v0.1d - 2018-03-30
+// - Bring up to date with minimp3.
+//
+// v0.1c - 2018-03-11
+// - Fix C++ build error.
+//
+// v0.1b - 2018-03-07
+// - Bring up to date with minimp3.
+//
+// v0.1a - 2018-02-28
+// - Fix compilation error on GCC/Clang.
+// - Fix some warnings.
+//
+// v0.1 - 2018-02-xx
+// - Initial versioned release.
+
+
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+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 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.
+
+For more information, please refer to
+*/
+
+/*
+ https://github.com/lieff/minimp3
+ To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide.
+ This software is distributed without any warranty.
+ See .
+*/
diff --git a/src/soloud/src/audiosource/wav/dr_wav.h b/src/soloud/src/audiosource/wav/dr_wav.h
new file mode 100644
index 0000000..79aa382
--- /dev/null
+++ b/src/soloud/src/audiosource/wav/dr_wav.h
@@ -0,0 +1,4254 @@
+// WAV audio loader and writer. Public domain. See "unlicense" statement at the end of this file.
+// dr_wav - v0.9.0-dev - 2018-xx-xx
+//
+// David Reid - mackron@gmail.com
+
+// USAGE
+//
+// This is a single-file library. To use it, do something like the following in one .c file.
+// #define DR_WAV_IMPLEMENTATION
+// #include "dr_wav.h"
+//
+// You can then #include this file in other parts of the program as you would with any other header file. Do something
+// like the following to read audio data:
+//
+// drwav wav;
+// if (!drwav_init_file(&wav, "my_song.wav")) {
+// // Error opening WAV file.
+// }
+//
+// drwav_int32* pDecodedInterleavedSamples = malloc(wav.totalSampleCount * sizeof(drwav_int32));
+// size_t numberOfSamplesActuallyDecoded = drwav_read_s32(&wav, wav.totalSampleCount, pDecodedInterleavedSamples);
+//
+// ...
+//
+// drwav_uninit(&wav);
+//
+// You can also use drwav_open() to allocate and initialize the loader for you:
+//
+// drwav* pWav = drwav_open_file("my_song.wav");
+// if (pWav == NULL) {
+// // Error opening WAV file.
+// }
+//
+// ...
+//
+// drwav_close(pWav);
+//
+// If you just want to quickly open and read the audio data in a single operation you can do something like this:
+//
+// unsigned int channels;
+// unsigned int sampleRate;
+// drwav_uint64 totalSampleCount;
+// float* pSampleData = drwav_open_file_and_read_f32("my_song.wav", &channels, &sampleRate, &totalSampleCount);
+// if (pSampleData == NULL) {
+// // Error opening and reading WAV file.
+// }
+//
+// ...
+//
+// drwav_free(pSampleData);
+//
+// The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in
+// this case), but you can still output the audio data in its internal format (see notes below for supported formats):
+//
+// size_t samplesRead = drwav_read(&wav, wav.totalSampleCount, pDecodedInterleavedSamples);
+//
+// You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for
+// a particular data format:
+//
+// size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
+//
+//
+// dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work
+// without any manual intervention.
+//
+//
+// dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at
+// drwav_open_write(), drwav_open_file_write(), etc. Use drwav_write() to write samples, or drwav_write_raw() to write
+// raw data in the "data" chunk.
+//
+// drwav_data_format format;
+// format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
+// format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
+// format.channels = 2;
+// format.sampleRate = 44100;
+// format.bitsPerSample = 16;
+// drwav* pWav = drwav_open_file_write("data/recording.wav", &format);
+//
+// ...
+//
+// drwav_uint64 samplesWritten = drwav_write(pWav, sampleCount, pSamples);
+//
+//
+//
+// OPTIONS
+// #define these options before including this file.
+//
+// #define DR_WAV_NO_CONVERSION_API
+// Disables conversion APIs such as drwav_read_f32() and drwav_s16_to_f32().
+//
+// #define DR_WAV_NO_STDIO
+// Disables drwav_open_file(), drwav_open_file_write(), etc.
+//
+//
+//
+// QUICK NOTES
+// - Samples are always interleaved.
+// - The default read function does not do any data conversion. Use drwav_read_f32() to read and convert audio data
+// to IEEE 32-bit floating point samples, drwav_read_s32() to read samples as signed 32-bit PCM and drwav_read_s16()
+// to read samples as signed 16-bit PCM. Tested and supported internal formats include the following:
+// - Unsigned 8-bit PCM
+// - Signed 12-bit PCM
+// - Signed 16-bit PCM
+// - Signed 24-bit PCM
+// - Signed 32-bit PCM
+// - IEEE 32-bit floating point
+// - IEEE 64-bit floating point
+// - A-law and u-law
+// - Microsoft ADPCM
+// - IMA ADPCM (DVI, format code 0x11)
+// - dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
+
+
+#ifndef dr_wav_h
+#define dr_wav_h
+
+#include
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+typedef signed char drwav_int8;
+typedef unsigned char drwav_uint8;
+typedef signed short drwav_int16;
+typedef unsigned short drwav_uint16;
+typedef signed int drwav_int32;
+typedef unsigned int drwav_uint32;
+typedef signed __int64 drwav_int64;
+typedef unsigned __int64 drwav_uint64;
+#else
+#include
+typedef int8_t drwav_int8;
+typedef uint8_t drwav_uint8;
+typedef int16_t drwav_int16;
+typedef uint16_t drwav_uint16;
+typedef int32_t drwav_int32;
+typedef uint32_t drwav_uint32;
+typedef int64_t drwav_int64;
+typedef uint64_t drwav_uint64;
+#endif
+typedef drwav_uint8 drwav_bool8;
+typedef drwav_uint32 drwav_bool32;
+#define DRWAV_TRUE 1
+#define DRWAV_FALSE 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Common data formats.
+#define DR_WAVE_FORMAT_PCM 0x1
+#define DR_WAVE_FORMAT_ADPCM 0x2
+#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
+#define DR_WAVE_FORMAT_ALAW 0x6
+#define DR_WAVE_FORMAT_MULAW 0x7
+#define DR_WAVE_FORMAT_DVI_ADPCM 0x11
+#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
+
+// Constants.
+#ifndef DRWAV_MAX_SMPL_LOOPS
+#define DRWAV_MAX_SMPL_LOOPS 1
+#endif
+
+// Flags to pass into drwav_init_ex(), etc.
+#define DRWAV_SEQUENTIAL 0x00000001
+
+typedef enum
+{
+ drwav_seek_origin_start,
+ drwav_seek_origin_current
+} drwav_seek_origin;
+
+typedef enum
+{
+ drwav_container_riff,
+ drwav_container_w64
+} drwav_container;
+
+typedef struct
+{
+ union
+ {
+ drwav_uint8 fourcc[4];
+ drwav_uint8 guid[16];
+ } id;
+
+ // The size in bytes of the chunk.
+ drwav_uint64 sizeInBytes;
+
+ // RIFF = 2 byte alignment.
+ // W64 = 8 byte alignment.
+ unsigned int paddingSize;
+} drwav_chunk_header;
+
+// Callback for when data is read. Return value is the number of bytes actually read.
+//
+// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
+// pBufferOut [out] The output buffer.
+// bytesToRead [in] The number of bytes to read.
+//
+// Returns the number of bytes actually read.
+//
+// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+// either the entire bytesToRead is filled or you have reached the end of the stream.
+typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+// Callback for when data is written. Returns value is the number of bytes actually written.
+//
+// pUserData [in] The user data that was passed to drwav_init_write(), drwav_open_write() and family.
+// pData [out] A pointer to the data to write.
+// bytesToWrite [in] The number of bytes to write.
+//
+// Returns the number of bytes actually written.
+//
+// If the return value differs from bytesToWrite, it indicates an error.
+typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
+
+// Callback for when data needs to be seeked.
+//
+// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
+// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
+// origin [in] The origin of the seek - the current position or the start of the stream.
+//
+// Returns whether or not the seek was successful.
+//
+// Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
+// will be either drwav_seek_origin_start or drwav_seek_origin_current.
+typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
+
+// Callback for when drwav_init_ex/drwav_open_ex finds a chunk.
+//
+// pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex(), drwav_open_ex() and family.
+// onRead [in] A pointer to the function to call when reading.
+// onSeek [in] A pointer to the function to call when seeking.
+// pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex(), drwav_open_ex() and family.
+// pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk.
+//
+// Returns the number of bytes read + seeked.
+//
+// To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same
+// for seeking with onSeek(). The return value must be the total number of bytes you have read _plus_ seeked.
+//
+// You must not attempt to read beyond the boundary of the chunk.
+typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader);
+
+// Structure for internal use. Only used for loaders opened with drwav_open_memory().
+typedef struct
+{
+ const drwav_uint8* data;
+ size_t dataSize;
+ size_t currentReadPos;
+} drwav__memory_stream;
+
+// Structure for internal use. Only used for writers opened with drwav_open_memory_write().
+typedef struct
+{
+ void** ppData;
+ size_t* pDataSize;
+ size_t dataSize;
+ size_t dataCapacity;
+ size_t currentWritePos;
+} drwav__memory_stream_write;
+
+typedef struct
+{
+ drwav_container container; // RIFF, W64.
+ drwav_uint32 format; // DR_WAVE_FORMAT_*
+ drwav_uint32 channels;
+ drwav_uint32 sampleRate;
+ drwav_uint32 bitsPerSample;
+} drwav_data_format;
+
+typedef struct
+{
+ // The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
+ // that require support for data formats not natively supported by dr_wav.
+ drwav_uint16 formatTag;
+
+ // The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc.
+ drwav_uint16 channels;
+
+ // The sample rate. Usually set to something like 44100.
+ drwav_uint32 sampleRate;
+
+ // Average bytes per second. You probably don't need this, but it's left here for informational purposes.
+ drwav_uint32 avgBytesPerSec;
+
+ // Block align. This is equal to the number of channels * bytes per sample.
+ drwav_uint16 blockAlign;
+
+ // Bits per sample.
+ drwav_uint16 bitsPerSample;
+
+ // The size of the extended data. Only used internally for validation, but left here for informational purposes.
+ drwav_uint16 extendedSize;
+
+ // The number of valid bits per sample. When is equal to WAVE_FORMAT_EXTENSIBLE,
+ // is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
+ // many bits a valid per sample. Mainly used for informational purposes.
+ drwav_uint16 validBitsPerSample;
+
+ // The channel mask. Not used at the moment.
+ drwav_uint32 channelMask;
+
+ // The sub-format, exactly as specified by the wave file.
+ drwav_uint8 subFormat[16];
+} drwav_fmt;
+
+typedef struct
+{
+ drwav_uint32 cuePointId;
+ drwav_uint32 type;
+ drwav_uint32 start;
+ drwav_uint32 end;
+ drwav_uint32 fraction;
+ drwav_uint32 playCount;
+} drwav_smpl_loop;
+
+ typedef struct
+{
+ drwav_uint32 manufacturer;
+ drwav_uint32 product;
+ drwav_uint32 samplePeriod;
+ drwav_uint32 midiUnityNotes;
+ drwav_uint32 midiPitchFraction;
+ drwav_uint32 smpteFormat;
+ drwav_uint32 smpteOffset;
+ drwav_uint32 numSampleLoops;
+ drwav_uint32 samplerData;
+ drwav_smpl_loop loops[DRWAV_MAX_SMPL_LOOPS];
+} drwav_smpl;
+
+typedef struct
+{
+ // A pointer to the function to call when more data is needed.
+ drwav_read_proc onRead;
+
+ // A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode.
+ drwav_write_proc onWrite;
+
+ // A pointer to the function to call when the wav file needs to be seeked.
+ drwav_seek_proc onSeek;
+
+ // The user data to pass to callbacks.
+ void* pUserData;
+
+
+ // Whether or not the WAV file is formatted as a standard RIFF file or W64.
+ drwav_container container;
+
+
+ // Structure containing format information exactly as specified by the wav file.
+ drwav_fmt fmt;
+
+ // The sample rate. Will be set to something like 44100.
+ drwav_uint32 sampleRate;
+
+ // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc.
+ drwav_uint16 channels;
+
+ // The bits per sample. Will be set to something like 16, 24, etc.
+ drwav_uint16 bitsPerSample;
+
+ // The number of bytes per sample.
+ drwav_uint16 bytesPerSample;
+
+ // Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE).
+ drwav_uint16 translatedFormatTag;
+
+ // The total number of samples making up the audio data. Use * to calculate
+ // the required size of a buffer to hold the entire audio data.
+ drwav_uint64 totalSampleCount;
+
+
+ // The size in bytes of the data chunk.
+ drwav_uint64 dataChunkDataSize;
+
+ // The position in the stream of the first byte of the data chunk. This is used for seeking.
+ drwav_uint64 dataChunkDataPos;
+
+ // The number of bytes remaining in the data chunk.
+ drwav_uint64 bytesRemaining;
+
+
+ // Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always
+ // set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.
+ drwav_uint64 dataChunkDataSizeTargetWrite;
+
+ // Keeps track of whether or not the wav writer was initialized in sequential mode.
+ drwav_bool32 isSequentialWrite;
+
+
+ // smpl chunk.
+ drwav_smpl smpl;
+
+
+ // A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory().
+ drwav__memory_stream memoryStream;
+ drwav__memory_stream_write memoryStreamWrite;
+
+ // Generic data for compressed formats. This data is shared across all block-compressed formats.
+ struct
+ {
+ drwav_uint64 iCurrentSample; // The index of the next sample that will be read by drwav_read_*(). This is used with "totalSampleCount" to ensure we don't read excess samples at the end of the last block.
+ } compressed;
+
+ // Microsoft ADPCM specific data.
+ struct
+ {
+ drwav_uint32 bytesRemainingInBlock;
+ drwav_uint16 predictor[2];
+ drwav_int32 delta[2];
+ drwav_int32 cachedSamples[4]; // Samples are stored in this cache during decoding.
+ drwav_uint32 cachedSampleCount;
+ drwav_int32 prevSamples[2][2]; // The previous 2 samples for each channel (2 channels at most).
+ } msadpcm;
+
+ // IMA ADPCM specific data.
+ struct
+ {
+ drwav_uint32 bytesRemainingInBlock;
+ drwav_int32 predictor[2];
+ drwav_int32 stepIndex[2];
+ drwav_int32 cachedSamples[16]; // Samples are stored in this cache during decoding.
+ drwav_uint32 cachedSampleCount;
+ } ima;
+} drwav;
+
+
+// Initializes a pre-allocated drwav object.
+//
+// pWav [out] A pointer to the drwav object being initialized.
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// onChunk [in, optional] The function to call when a chunk is enumerated at initialized time.
+// pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+// pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk.
+// flags [in, optional] A set of flags for controlling how things are loaded.
+//
+// Returns true if successful; false otherwise.
+//
+// Close the loader with drwav_uninit().
+//
+// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
+// a drwav object on the heap and return a pointer to it.
+//
+// Possible values for flags:
+// DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function
+// to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored.
+//
+// drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);".
+//
+// The onChunk is callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt
+// after the function returns.
+//
+// See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
+drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
+drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags);
+
+// Initializes a pre-allocated drwav object for writing.
+//
+// onWrite [in] The function to call when data needs to be written.
+// onSeek [in] The function to call when the write position needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+//
+// Returns true if successful; false otherwise.
+//
+// Close the writer with drwav_uninit().
+//
+// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform
+// a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.
+//
+// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
+// a drwav object on the heap and return a pointer to it.
+//
+// See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
+drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData);
+
+// Uninitializes the given drwav object.
+//
+// Use this only for objects initialized with drwav_init().
+void drwav_uninit(drwav* pWav);
+
+
+// Opens a wav file using the given callbacks.
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns null on error.
+//
+// Close the loader with drwav_close().
+//
+// You can also use drwav_open_file() and drwav_open_memory() to open the stream from a file or from a block of
+// memory respectively.
+//
+// This is different from drwav_init() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
+// initializing it.
+//
+// See also: drwav_init(), drwav_open_file(), drwav_open_memory(), drwav_close()
+drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
+drwav* drwav_open_ex(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags);
+
+// Opens a wav file for writing using the given callbacks.
+//
+// onWrite [in] The function to call when data needs to be written.
+// onSeek [in] The function to call when the write position needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+//
+// Returns null on error.
+//
+// Close the loader with drwav_close().
+//
+// You can also use drwav_open_file_write() and drwav_open_memory_write() to open the stream from a file or from a block
+// of memory respectively.
+//
+// This is different from drwav_init_write() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
+// initializing it.
+//
+// See also: drwav_open_file_write(), drwav_open_memory_write(), drwav_close()
+drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+drwav* drwav_open_write_sequential(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData);
+
+// Uninitializes and deletes the the given drwav object.
+//
+// Use this only for objects created with drwav_open().
+void drwav_close(drwav* pWav);
+
+
+// Reads raw audio data.
+//
+// This is the lowest level function for reading audio data. It simply reads the given number of
+// bytes of the raw internal sample data.
+//
+// Consider using drwav_read_s16(), drwav_read_s32() or drwav_read_f32() for reading sample data in
+// a consistent format.
+//
+// Returns the number of bytes actually read.
+size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
+
+// Reads a chunk of audio data in the native internal format.
+//
+// This is typically the most efficient way to retrieve audio data, but it does not do any format
+// conversions which means you'll need to convert the data manually if required.
+//
+// If the return value is less than it means the end of the file has been reached or
+// you have requested more samples than can possibly fit in the output buffer.
+//
+// This function will only work when sample data is of a fixed size and uncompressed. If you are
+// using a compressed format consider using drwav_read_raw() or drwav_read_s16/s32/f32/etc().
+drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut);
+
+// Same as drwav_read(), except works on PCM frames instead of samples. Returns the number of PCM
+// freames read.
+drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
+
+// Seeks to the given sample.
+//
+// Returns true if successful; false otherwise.
+drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample);
+
+// Same as drwav_seek_to_sample() except workd on PCM frames.
+drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);
+
+
+// Writes raw audio data.
+//
+// Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
+size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
+
+// Writes audio data based on sample counts.
+//
+// Returns the number of samples written.
+drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData);
+
+// Same as drwav_write(), but works on PCM frames instead of samples. Returns the number of PCM frames written.
+drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
+
+
+//// Conversion Utilities ////
+#ifndef DR_WAV_NO_CONVERSION_API
+
+// Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than it means the end of the file has been reached.
+drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+
+// Same as drwav_read_s16(), except works on PCM frames instead of samples.
+drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples.
+void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples.
+void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples.
+void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples.
+void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples.
+void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to signed 16-bit PCM samples.
+void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to signed 16-bit PCM samples.
+void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+
+// Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than it means the end of the file has been reached.
+drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut);
+
+// Same as drwav_read_f32(), except works on PCM frames instead of samples.
+drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples.
+void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to IEEE 32-bit floating point samples.
+void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to IEEE 32-bit floating point samples.
+void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+
+// Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than it means the end of the file has been reached.
+drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut);
+
+// Same as drwav_read_s32(), except works on PCM frames instead of samples.
+drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples.
+void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples.
+void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples.
+void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples.
+void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples.
+void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to signed 32-bit PCM samples.
+void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to signed 32-bit PCM samples.
+void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+#endif //DR_WAV_NO_CONVERSION_API
+
+
+//// High-Level Convenience Helpers ////
+
+#ifndef DR_WAV_NO_STDIO
+
+// Helper for initializing a wave file using stdio.
+//
+// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav_bool32 drwav_init_file(drwav* pWav, const char* filename);
+drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags);
+
+// Helper for initializing a wave file for writing using stdio.
+//
+// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat);
+drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+// Helper for opening a wave file using stdio.
+//
+// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav* drwav_open_file(const char* filename);
+drwav* drwav_open_file_ex(const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags);
+
+// Helper for opening a wave file for writing using stdio.
+//
+// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat);
+drwav* drwav_open_file_write_sequential(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+#endif //DR_WAV_NO_STDIO
+
+// Helper for initializing a loader from a pre-allocated memory buffer.
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the drwav object.
+//
+// The buffer should contain the contents of the entire wave file, not just the sample data.
+drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize);
+drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags);
+
+// Helper for initializing a writer which outputs data to a memory buffer.
+//
+// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+//
+// The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be
+// considered valid until after drwav_uninit() has been called anyway.
+drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
+drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+// Helper for opening a loader from a pre-allocated memory buffer.
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the drwav object.
+//
+// The buffer should contain the contents of the entire wave file, not just the sample data.
+drwav* drwav_open_memory(const void* data, size_t dataSize);
+drwav* drwav_open_memory_ex(const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags);
+
+// Helper for opening a writer which outputs data to a memory buffer.
+//
+// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+//
+// The buffer will remain allocated even after drwav_close() is called. Indeed, the buffer should not be
+// considered valid until after drwav_close() has been called anyway.
+drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
+drwav* drwav_open_memory_write_sequential(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+
+#ifndef DR_WAV_NO_CONVERSION_API
+// Opens and reads a wav file in a single operation.
+drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+#ifndef DR_WAV_NO_STDIO
+// Opens and decodes a wav file in a single operation.
+drwav_int16* drwav_open_file_and_read_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+float* drwav_open_file_and_read_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+drwav_int32* drwav_open_file_and_read_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+#endif
+
+// Opens and decodes a wav file from a block of memory in a single operation.
+drwav_int16* drwav_open_memory_and_read_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+float* drwav_open_memory_and_read_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+drwav_int32* drwav_open_memory_and_read_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount);
+#endif
+
+// Frees data that was allocated internally by dr_wav.
+void drwav_free(void* pDataReturnedByOpenAndRead);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // dr_wav_h
+
+
+/////////////////////////////////////////////////////
+//
+// IMPLEMENTATION
+//
+/////////////////////////////////////////////////////
+
+#ifdef DR_WAV_IMPLEMENTATION
+#include
+#include // For memcpy(), memset()
+#include // For INT_MAX
+
+#ifndef DR_WAV_NO_STDIO
+#include
+#endif
+
+// Standard library stuff.
+#ifndef DRWAV_ASSERT
+#include
+#define DRWAV_ASSERT(expression) assert(expression)
+#endif
+#ifndef DRWAV_MALLOC
+#define DRWAV_MALLOC(sz) malloc((sz))
+#endif
+#ifndef DRWAV_REALLOC
+#define DRWAV_REALLOC(p, sz) realloc((p), (sz))
+#endif
+#ifndef DRWAV_FREE
+#define DRWAV_FREE(p) free((p))
+#endif
+#ifndef DRWAV_COPY_MEMORY
+#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
+#endif
+#ifndef DRWAV_ZERO_MEMORY
+#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
+#endif
+
+#define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
+#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
+#define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
+#define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
+#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
+
+#define drwav_assert DRWAV_ASSERT
+#define drwav_copy_memory DRWAV_COPY_MEMORY
+#define drwav_zero_memory DRWAV_ZERO_MEMORY
+
+typedef drwav_int32 drwav_result;
+#define DRWAV_SUCCESS 0
+#define DRWAV_ERROR -1
+#define DRWAV_INVALID_ARGS -2
+#define DRWAV_INVALID_OPERATION -3
+#define DRWAV_INVALID_FILE -100
+#define DRWAV_EOF -101
+
+#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future.
+
+#ifdef _MSC_VER
+#define DRWAV_INLINE __forceinline
+#else
+#ifdef __GNUC__
+#define DRWAV_INLINE inline __attribute__((always_inline))
+#else
+#define DRWAV_INLINE inline
+#endif
+#endif
+
+#if defined(SIZE_MAX)
+ #define DRWAV_SIZE_MAX SIZE_MAX
+#else
+ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+ #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRWAV_SIZE_MAX 0xFFFFFFFF
+ #endif
+#endif
+
+static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; // 66666972-912E-11CF-A5D6-28DB04C10000
+static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 65766177-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 74636166-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 61746164-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A
+
+static DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
+{
+ const drwav_uint32* a32 = (const drwav_uint32*)a;
+ const drwav_uint32* b32 = (const drwav_uint32*)b;
+
+ return
+ a32[0] == b32[0] &&
+ a32[1] == b32[1] &&
+ a32[2] == b32[2] &&
+ a32[3] == b32[3];
+}
+
+static DRWAV_INLINE drwav_bool32 drwav__fourcc_equal(const unsigned char* a, const char* b)
+{
+ return
+ a[0] == b[0] &&
+ a[1] == b[1] &&
+ a[2] == b[2] &&
+ a[3] == b[3];
+}
+
+
+
+static DRWAV_INLINE int drwav__is_little_endian()
+{
+ int n = 1;
+ return (*(char*)&n) == 1;
+}
+
+static DRWAV_INLINE unsigned short drwav__bytes_to_u16(const unsigned char* data)
+{
+ return (data[0] << 0) | (data[1] << 8);
+}
+
+static DRWAV_INLINE short drwav__bytes_to_s16(const unsigned char* data)
+{
+ return (short)drwav__bytes_to_u16(data);
+}
+
+static DRWAV_INLINE unsigned int drwav__bytes_to_u32(const unsigned char* data)
+{
+ return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+}
+
+static DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const unsigned char* data)
+{
+ return
+ ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
+ ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
+}
+
+static DRWAV_INLINE void drwav__bytes_to_guid(const unsigned char* data, drwav_uint8* guid)
+{
+ for (int i = 0; i < 16; ++i) {
+ guid[i] = data[i];
+ }
+}
+
+
+static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
+{
+ return
+ formatTag == DR_WAVE_FORMAT_ADPCM ||
+ formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
+}
+
+
+drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+drwav* drwav_open_write__internal(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+
+static drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
+{
+ if (container == drwav_container_riff) {
+ if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
+ return DRWAV_EOF;
+ }
+
+ unsigned char sizeInBytes[4];
+ if (onRead(pUserData, sizeInBytes, 4) != 4) {
+ return DRWAV_INVALID_FILE;
+ }
+
+ pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes);
+ pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 2);
+ *pRunningBytesReadOut += 8;
+ } else {
+ if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
+ return DRWAV_EOF;
+ }
+
+ unsigned char sizeInBytes[8];
+ if (onRead(pUserData, sizeInBytes, 8) != 8) {
+ return DRWAV_INVALID_FILE;
+ }
+
+ pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; // <-- Subtract 24 because w64 includes the size of the header.
+ pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 8);
+ *pRunningBytesReadOut += 24;
+ }
+
+ return DRWAV_SUCCESS;
+}
+
+static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
+{
+ drwav_uint64 bytesRemainingToSeek = offset;
+ while (bytesRemainingToSeek > 0) {
+ if (bytesRemainingToSeek > 0x7FFFFFFF) {
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ bytesRemainingToSeek -= 0x7FFFFFFF;
+ } else {
+ if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ bytesRemainingToSeek = 0;
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+static drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
+{
+ if (offset <= 0x7FFFFFFF) {
+ return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
+ }
+
+ // Larger than 32-bit seek.
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
+ return DRWAV_FALSE;
+ }
+ offset -= 0x7FFFFFFF;
+
+ for (;;) {
+ if (offset <= 0x7FFFFFFF) {
+ return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
+ }
+
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ offset -= 0x7FFFFFFF;
+ }
+
+ // Should never get here.
+ //return DRWAV_TRUE;
+}
+
+
+static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)
+{
+ drwav_chunk_header header;
+ if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
+ return DRWAV_FALSE;
+ }
+
+
+ // Skip non-fmt chunks.
+ while ((container == drwav_container_riff && !drwav__fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
+ if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
+
+ // Try the next header.
+ if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ // Validation.
+ if (container == drwav_container_riff) {
+ if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) {
+ return DRWAV_FALSE;
+ }
+ } else {
+ if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ unsigned char fmt[16];
+ if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += sizeof(fmt);
+
+ fmtOut->formatTag = drwav__bytes_to_u16(fmt + 0);
+ fmtOut->channels = drwav__bytes_to_u16(fmt + 2);
+ fmtOut->sampleRate = drwav__bytes_to_u32(fmt + 4);
+ fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8);
+ fmtOut->blockAlign = drwav__bytes_to_u16(fmt + 12);
+ fmtOut->bitsPerSample = drwav__bytes_to_u16(fmt + 14);
+
+ fmtOut->extendedSize = 0;
+ fmtOut->validBitsPerSample = 0;
+ fmtOut->channelMask = 0;
+ memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
+
+ if (header.sizeInBytes > 16) {
+ unsigned char fmt_cbSize[2];
+ if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
+ return DRWAV_FALSE; // Expecting more data.
+ }
+ *pRunningBytesReadOut += sizeof(fmt_cbSize);
+
+ int bytesReadSoFar = 18;
+
+ fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize);
+ if (fmtOut->extendedSize > 0) {
+ // Simple validation.
+ if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ if (fmtOut->extendedSize != 22) {
+ return DRWAV_FALSE;
+ }
+ }
+
+ if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ unsigned char fmtext[22];
+ if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
+ return DRWAV_FALSE; // Expecting more data.
+ }
+
+ fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0);
+ fmtOut->channelMask = drwav__bytes_to_u32(fmtext + 2);
+ drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat);
+ } else {
+ if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ }
+ *pRunningBytesReadOut += fmtOut->extendedSize;
+
+ bytesReadSoFar += fmtOut->extendedSize;
+ }
+
+ // Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size.
+ if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);
+ }
+
+ if (header.paddingSize > 0) {
+ if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += header.paddingSize;
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+#ifndef DR_WAV_NO_STDIO
+FILE* drwav_fopen(const char* filePath, const char* openMode)
+{
+ FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (fopen_s(&pFile, filePath, openMode) != 0) {
+ return DRWAV_FALSE;
+ }
+#else
+ pFile = fopen(filePath, openMode);
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+#endif
+
+ return pFile;
+}
+
+static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
+}
+
+static size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
+{
+ return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
+}
+
+static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+drwav_bool32 drwav_init_file(drwav* pWav, const char* filename)
+{
+ return drwav_init_file_ex(pWav, filename, NULL, NULL, 0);
+}
+
+drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
+ FILE* pFile = drwav_fopen(filename, "rb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_ex(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, onChunk, (void*)pFile, pChunkUserData, flags);
+}
+
+
+drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
+{
+ FILE* pFile = drwav_fopen(filename, "wb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
+}
+
+drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat)
+{
+ return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE);
+}
+
+drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE);
+}
+
+drwav* drwav_open_file(const char* filename)
+{
+ return drwav_open_file_ex(filename, NULL, NULL, 0);
+}
+
+drwav* drwav_open_file_ex(const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
+ FILE* pFile = drwav_fopen(filename, "rb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ drwav* pWav = drwav_open_ex(drwav__on_read_stdio, drwav__on_seek_stdio, onChunk, (void*)pFile, pChunkUserData, flags);
+ if (pWav == NULL) {
+ fclose(pFile);
+ return NULL;
+ }
+
+ return pWav;
+}
+
+
+drwav* drwav_open_file_write__internal(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
+{
+ FILE* pFile = drwav_fopen(filename, "wb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ drwav* pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
+ if (pWav == NULL) {
+ fclose(pFile);
+ return NULL;
+ }
+
+ return pWav;
+}
+
+drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat)
+{
+ return drwav_open_file_write__internal(filename, pFormat, 0, DRWAV_FALSE);
+}
+
+drwav* drwav_open_file_write_sequential(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ return drwav_open_file_write__internal(filename, pFormat, totalSampleCount, DRWAV_TRUE);
+}
+#endif //DR_WAV_NO_STDIO
+
+
+static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ drwav__memory_stream* memory = (drwav__memory_stream*)pUserData;
+ drwav_assert(memory != NULL);
+ drwav_assert(memory->dataSize >= memory->currentReadPos);
+
+ size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
+ if (bytesToRead > bytesRemaining) {
+ bytesToRead = bytesRemaining;
+ }
+
+ if (bytesToRead > 0) {
+ DRWAV_COPY_MEMORY(pBufferOut, memory->data + memory->currentReadPos, bytesToRead);
+ memory->currentReadPos += bytesToRead;
+ }
+
+ return bytesToRead;
+}
+
+static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ drwav__memory_stream* memory = (drwav__memory_stream*)pUserData;
+ drwav_assert(memory != NULL);
+
+ if (origin == drwav_seek_origin_current) {
+ if (offset > 0) {
+ if (memory->currentReadPos + offset > memory->dataSize) {
+ return DRWAV_FALSE; // Trying to seek too far forward.
+ }
+ } else {
+ if (memory->currentReadPos < (size_t)-offset) {
+ return DRWAV_FALSE; // Trying to seek too far backwards.
+ }
+ }
+
+ // This will never underflow thanks to the clamps above.
+ memory->currentReadPos += offset;
+ } else {
+ if ((drwav_uint32)offset <= memory->dataSize) {
+ memory->currentReadPos = offset;
+ } else {
+ return DRWAV_FALSE; // Trying to seek too far forward.
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
+{
+ drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
+ drwav_assert(memory != NULL);
+ drwav_assert(memory->dataCapacity >= memory->currentWritePos);
+
+ size_t bytesRemaining = memory->dataCapacity - memory->currentWritePos;
+ if (bytesRemaining < bytesToWrite) {
+ // Need to reallocate.
+ size_t newDataCapacity = (memory->dataCapacity == 0) ? 256 : memory->dataCapacity * 2;
+
+ // If doubling wasn't enough, just make it the minimum required size to write the data.
+ if ((newDataCapacity - memory->currentWritePos) < bytesToWrite) {
+ newDataCapacity = memory->currentWritePos + bytesToWrite;
+ }
+
+ void* pNewData = DRWAV_REALLOC(*memory->ppData, newDataCapacity);
+ if (pNewData == NULL) {
+ return 0;
+ }
+
+ *memory->ppData = pNewData;
+ memory->dataCapacity = newDataCapacity;
+ }
+
+ drwav_uint8* pDataOut = (drwav_uint8*)(*memory->ppData);
+ DRWAV_COPY_MEMORY(pDataOut + memory->currentWritePos, pDataIn, bytesToWrite);
+
+ memory->currentWritePos += bytesToWrite;
+ if (memory->dataSize < memory->currentWritePos) {
+ memory->dataSize = memory->currentWritePos;
+ }
+
+ *memory->pDataSize = memory->dataSize;
+
+ return bytesToWrite;
+}
+
+static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
+ drwav_assert(memory != NULL);
+
+ if (origin == drwav_seek_origin_current) {
+ if (offset > 0) {
+ if (memory->currentWritePos + offset > memory->dataSize) {
+ offset = (int)(memory->dataSize - memory->currentWritePos); // Trying to seek too far forward.
+ }
+ } else {
+ if (memory->currentWritePos < (size_t)-offset) {
+ offset = -(int)memory->currentWritePos; // Trying to seek too far backwards.
+ }
+ }
+
+ // This will never underflow thanks to the clamps above.
+ memory->currentWritePos += offset;
+ } else {
+ if ((drwav_uint32)offset <= memory->dataSize) {
+ memory->currentWritePos = offset;
+ } else {
+ memory->currentWritePos = memory->dataSize; // Trying to seek too far forward.
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize)
+{
+ return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0);
+}
+
+drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
+ if (data == NULL || dataSize == 0) {
+ return DRWAV_FALSE;
+ }
+
+ drwav__memory_stream memoryStream;
+ drwav_zero_memory(&memoryStream, sizeof(memoryStream));
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+
+ if (!drwav_init_ex(pWav, drwav__on_read_memory, drwav__on_seek_memory, onChunk, (void*)&memoryStream, pChunkUserData, flags)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->memoryStream = memoryStream;
+ pWav->pUserData = &pWav->memoryStream;
+ return DRWAV_TRUE;
+}
+
+
+drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
+{
+ if (ppData == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ *ppData = NULL; // Important because we're using realloc()!
+ *pDataSize = 0;
+
+ drwav__memory_stream_write memoryStreamWrite;
+ drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
+ memoryStreamWrite.ppData = ppData;
+ memoryStreamWrite.pDataSize = pDataSize;
+ memoryStreamWrite.dataSize = 0;
+ memoryStreamWrite.dataCapacity = 0;
+ memoryStreamWrite.currentWritePos = 0;
+
+ if (!drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->memoryStreamWrite = memoryStreamWrite;
+ pWav->pUserData = &pWav->memoryStreamWrite;
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat)
+{
+ return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE);
+}
+
+drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE);
+}
+
+
+drwav* drwav_open_memory(const void* data, size_t dataSize)
+{
+ return drwav_open_memory_ex(data, dataSize, NULL, NULL, 0);
+}
+
+drwav* drwav_open_memory_ex(const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
+ if (data == NULL || dataSize == 0) {
+ return NULL;
+ }
+
+ drwav__memory_stream memoryStream;
+ drwav_zero_memory(&memoryStream, sizeof(memoryStream));
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+
+ drwav* pWav = drwav_open_ex(drwav__on_read_memory, drwav__on_seek_memory, onChunk, (void*)&memoryStream, pChunkUserData, flags);
+ if (pWav == NULL) {
+ return NULL;
+ }
+
+ pWav->memoryStream = memoryStream;
+ pWav->pUserData = &pWav->memoryStream;
+ return pWav;
+}
+
+
+drwav* drwav_open_memory_write__internal(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
+{
+ if (ppData == NULL) {
+ return NULL;
+ }
+
+ *ppData = NULL; // Important because we're using realloc()!
+ *pDataSize = 0;
+
+ drwav__memory_stream_write memoryStreamWrite;
+ drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
+ memoryStreamWrite.ppData = ppData;
+ memoryStreamWrite.pDataSize = pDataSize;
+ memoryStreamWrite.dataSize = 0;
+ memoryStreamWrite.dataCapacity = 0;
+ memoryStreamWrite.currentWritePos = 0;
+
+ drwav* pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite);
+ if (pWav == NULL) {
+ return NULL;
+ }
+
+ pWav->memoryStreamWrite = memoryStreamWrite;
+ pWav->pUserData = &pWav->memoryStreamWrite;
+ return pWav;
+}
+
+drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat)
+{
+ return drwav_open_memory_write__internal(ppData, pDataSize, pFormat, 0, DRWAV_FALSE);
+}
+
+drwav* drwav_open_memory_write_sequential(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ return drwav_open_memory_write__internal(ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE);
+}
+
+
+size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
+{
+ drwav_assert(onRead != NULL);
+ drwav_assert(pCursor != NULL);
+
+ size_t bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
+ *pCursor += bytesRead;
+ return bytesRead;
+}
+
+drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
+{
+ drwav_assert(onSeek != NULL);
+ drwav_assert(pCursor != NULL);
+
+ if (!onSeek(pUserData, offset, origin)) {
+ return DRWAV_FALSE;
+ }
+
+ if (origin == drwav_seek_origin_start) {
+ *pCursor = offset;
+ } else {
+ *pCursor += offset;
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
+{
+ return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);
+}
+
+drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags)
+{
+ if (onRead == NULL || onSeek == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ drwav_uint64 cursor = 0; // <-- Keeps track of the byte position so we can seek to specific locations.
+ drwav_bool32 sequential = (flags & DRWAV_SEQUENTIAL) != 0;
+
+ drwav_zero_memory(pWav, sizeof(*pWav));
+ pWav->onRead = onRead;
+ pWav->onSeek = onSeek;
+ pWav->pUserData = pReadSeekUserData;
+
+ // The first 4 bytes should be the RIFF identifier.
+ unsigned char riff[4];
+ if (drwav__on_read(onRead, pReadSeekUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
+ return DRWAV_FALSE;
+ }
+
+ // The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
+ // w64 it will start with "riff".
+ if (drwav__fourcc_equal(riff, "RIFF")) {
+ pWav->container = drwav_container_riff;
+ } else if (drwav__fourcc_equal(riff, "riff")) {
+ pWav->container = drwav_container_w64;
+
+ // Check the rest of the GUID for validity.
+ drwav_uint8 riff2[12];
+ if (drwav__on_read(onRead, pReadSeekUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
+ return DRWAV_FALSE;
+ }
+
+ for (int i = 0; i < 12; ++i) {
+ if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
+ return DRWAV_FALSE;
+ }
+ }
+ } else {
+ return DRWAV_FALSE; // Unknown or unsupported container.
+ }
+
+
+ if (pWav->container == drwav_container_riff) {
+ // RIFF/WAVE
+ unsigned char chunkSizeBytes[4];
+ if (drwav__on_read(onRead, pReadSeekUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
+ return DRWAV_FALSE;
+ }
+
+ unsigned int chunkSize = drwav__bytes_to_u32(chunkSizeBytes);
+ if (chunkSize < 36) {
+ return DRWAV_FALSE; // Chunk size should always be at least 36 bytes.
+ }
+
+ unsigned char wave[4];
+ if (drwav__on_read(onRead, pReadSeekUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
+ return DRWAV_FALSE;
+ }
+
+ if (!drwav__fourcc_equal(wave, "WAVE")) {
+ return DRWAV_FALSE; // Expecting "WAVE".
+ }
+ } else {
+ // W64
+ unsigned char chunkSize[8];
+ if (drwav__on_read(onRead, pReadSeekUserData, chunkSize, sizeof(chunkSize), &cursor) != sizeof(chunkSize)) {
+ return DRWAV_FALSE;
+ }
+
+ if (drwav__bytes_to_u64(chunkSize) < 80) {
+ return DRWAV_FALSE;
+ }
+
+ drwav_uint8 wave[16];
+ if (drwav__on_read(onRead, pReadSeekUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
+ return DRWAV_FALSE;
+ }
+
+ if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ // The next bytes should be the "fmt " chunk.
+ drwav_fmt fmt;
+ if (!drwav__read_fmt(onRead, onSeek, pReadSeekUserData, pWav->container, &cursor, &fmt)) {
+ return DRWAV_FALSE; // Failed to read the "fmt " chunk.
+ }
+
+ // Basic validation.
+ if (fmt.sampleRate == 0 || fmt.channels == 0 || fmt.bitsPerSample == 0 || fmt.blockAlign == 0) {
+ return DRWAV_FALSE; // Invalid channel count. Probably an invalid WAV file.
+ }
+
+
+ // Translate the internal format.
+ unsigned short translatedFormatTag = fmt.formatTag;
+ if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0);
+ }
+
+
+
+ drwav_uint64 sampleCountFromFactChunk = 0;
+
+ // We need to enumerate over each chunk for two reasons:
+ // 1) The "data" chunk may not be the next one
+ // 2) We may want to report each chunk back to the client
+ //
+ // In order to correctly report each chunk back to the client we will need to keep looping until the end of the file.
+ drwav_bool32 foundDataChunk = DRWAV_FALSE;
+ drwav_uint64 dataChunkSize = 0;
+
+ // The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop.
+ drwav_uint64 chunkSize = 0;
+ for (;;)
+ {
+ drwav_chunk_header header;
+ drwav_result result = drwav__read_chunk_header(onRead, pReadSeekUserData, pWav->container, &cursor, &header);
+ if (result != DRWAV_SUCCESS) {
+ if (!foundDataChunk) {
+ return DRWAV_FALSE;
+ } else {
+ break; // Probably at the end of the file. Get out of the loop.
+ }
+ }
+
+ // Tell the client about this chunk.
+ if (!sequential && onChunk != NULL) {
+ drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, onRead, onSeek, pReadSeekUserData, &header);
+
+ // dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before
+ // we called the callback.
+ if (callbackBytesRead > 0) {
+ if (!drwav__seek_from_start(onSeek, cursor, pReadSeekUserData)) {
+ return DRWAV_FALSE;
+ }
+ }
+ }
+
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+
+ chunkSize = header.sizeInBytes;
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "data")) {
+ foundDataChunk = DRWAV_TRUE;
+ dataChunkSize = chunkSize;
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
+ foundDataChunk = DRWAV_TRUE;
+ dataChunkSize = chunkSize;
+ }
+ }
+
+ // If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for
+ // this is that we would otherwise require a backwards seek which sequential mode forbids.
+ if (foundDataChunk && sequential) {
+ break;
+ }
+
+ // Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats.
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "fact")) {
+ drwav_uint32 sampleCount;
+ if (drwav__on_read(onRead, pReadSeekUserData, &sampleCount, 4, &cursor) != 4) {
+ return DRWAV_FALSE;
+ }
+ chunkSize -= 4;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+
+ // The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
+ // for Microsoft ADPCM formats.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ sampleCountFromFactChunk = sampleCount;
+ } else {
+ sampleCountFromFactChunk = 0;
+ }
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
+ if (drwav__on_read(onRead, pReadSeekUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
+ return DRWAV_FALSE;
+ }
+ chunkSize -= 8;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+ }
+ }
+
+ // "smpl" chunk.
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "smpl")) {
+ unsigned char smplHeaderData[36]; // 36 = size of the smpl header section, not including the loop data.
+ if (chunkSize >= sizeof(smplHeaderData)) {
+ drwav_uint64 bytesJustRead = drwav__on_read(onRead, pReadSeekUserData, smplHeaderData, sizeof(smplHeaderData), &cursor);
+ chunkSize -= bytesJustRead;
+
+ if (bytesJustRead == sizeof(smplHeaderData)) {
+ pWav->smpl.manufacturer = drwav__bytes_to_u32(smplHeaderData+0);
+ pWav->smpl.product = drwav__bytes_to_u32(smplHeaderData+4);
+ pWav->smpl.samplePeriod = drwav__bytes_to_u32(smplHeaderData+8);
+ pWav->smpl.midiUnityNotes = drwav__bytes_to_u32(smplHeaderData+12);
+ pWav->smpl.midiPitchFraction = drwav__bytes_to_u32(smplHeaderData+16);
+ pWav->smpl.smpteFormat = drwav__bytes_to_u32(smplHeaderData+20);
+ pWav->smpl.smpteOffset = drwav__bytes_to_u32(smplHeaderData+24);
+ pWav->smpl.numSampleLoops = drwav__bytes_to_u32(smplHeaderData+28);
+ pWav->smpl.samplerData = drwav__bytes_to_u32(smplHeaderData+32);
+
+ for (drwav_uint32 iLoop = 0; iLoop < pWav->smpl.numSampleLoops && iLoop < drwav_countof(pWav->smpl.loops); ++iLoop) {
+ unsigned char smplLoopData[24]; // 24 = size of a loop section in the smpl chunk.
+ bytesJustRead = drwav__on_read(onRead, pReadSeekUserData, smplLoopData, sizeof(smplLoopData), &cursor);
+ chunkSize -= bytesJustRead;
+
+ if (bytesJustRead == sizeof(smplLoopData)) {
+ pWav->smpl.loops[iLoop].cuePointId = drwav__bytes_to_u32(smplLoopData+0);
+ pWav->smpl.loops[iLoop].type = drwav__bytes_to_u32(smplLoopData+4);
+ pWav->smpl.loops[iLoop].start = drwav__bytes_to_u32(smplLoopData+8);
+ pWav->smpl.loops[iLoop].end = drwav__bytes_to_u32(smplLoopData+12);
+ pWav->smpl.loops[iLoop].fraction = drwav__bytes_to_u32(smplLoopData+16);
+ pWav->smpl.loops[iLoop].playCount = drwav__bytes_to_u32(smplLoopData+20);
+ } else {
+ break; // Break from the smpl loop for loop.
+ }
+ }
+ }
+ } else {
+ // Looks like invalid data. Ignore the chunk.
+ }
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_SMPL)) {
+ // This path will be hit when a W64 WAV file contains a smpl chunk. I don't have a sample file to test this path, so a contribution
+ // is welcome to add support for this.
+ }
+ }
+
+ // Make sure we seek past the padding.
+ chunkSize += header.paddingSize;
+ if (!drwav__seek_forward(onSeek, chunkSize, pReadSeekUserData)) {
+ break;
+ }
+ cursor += chunkSize;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+ }
+
+ // If we haven't found a data chunk, return an error.
+ if (!foundDataChunk) {
+ return DRWAV_FALSE;
+ }
+
+ // We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk.
+ if (!sequential) {
+ if (!drwav__seek_from_start(onSeek, pWav->dataChunkDataPos, pReadSeekUserData)) {
+ return DRWAV_FALSE;
+ }
+ cursor = pWav->dataChunkDataPos;
+ }
+
+
+ // At this point we should be sitting on the first byte of the raw audio data.
+
+ pWav->fmt = fmt;
+ pWav->sampleRate = fmt.sampleRate;
+ pWav->channels = fmt.channels;
+ pWav->bitsPerSample = fmt.bitsPerSample;
+ pWav->bytesPerSample = fmt.blockAlign / fmt.channels;
+ pWav->bytesRemaining = dataChunkSize;
+ pWav->translatedFormatTag = translatedFormatTag;
+ pWav->dataChunkDataSize = dataChunkSize;
+
+ // The bytes per sample should never be 0 at this point. This would indicate an invalid WAV file.
+ if (pWav->bytesPerSample == 0) {
+ return DRWAV_FALSE;
+ }
+
+ if (sampleCountFromFactChunk != 0) {
+ pWav->totalSampleCount = sampleCountFromFactChunk * fmt.channels;
+ } else {
+ pWav->totalSampleCount = dataChunkSize / pWav->bytesPerSample;
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte.
+ }
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels);
+ }
+ }
+
+ // The way we calculate the bytes per sample does not make sense for compressed formats so we just set it to 0.
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ pWav->bytesPerSample = 0;
+ }
+
+ // Some formats only support a certain number of channels.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ if (pWav->channels > 2) {
+ return DRWAV_FALSE;
+ }
+ }
+
+#ifdef DR_WAV_LIBSNDFILE_COMPAT
+ // I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
+ // it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
+ // from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct
+ // way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should
+ // always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my
+ // correctness tests against libsndfile, and is disabled by default.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte.
+ }
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels);
+ }
+#endif
+
+ return DRWAV_TRUE;
+}
+
+
+drwav_uint32 drwav_riff_chunk_size_riff(drwav_uint64 dataChunkSize)
+{
+ if (dataChunkSize <= (0xFFFFFFFF - 36)) {
+ return 36 + (drwav_uint32)dataChunkSize;
+ } else {
+ return 0xFFFFFFFF;
+ }
+}
+
+drwav_uint32 drwav_data_chunk_size_riff(drwav_uint64 dataChunkSize)
+{
+ if (dataChunkSize <= 0xFFFFFFFF) {
+ return (drwav_uint32)dataChunkSize;
+ } else {
+ return 0xFFFFFFFF;
+ }
+}
+
+drwav_uint64 drwav_riff_chunk_size_w64(drwav_uint64 dataChunkSize)
+{
+ return 80 + 24 + dataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+}
+
+drwav_uint64 drwav_data_chunk_size_w64(drwav_uint64 dataChunkSize)
+{
+ return 24 + dataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+}
+
+
+drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+ if (pWav == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ if (onWrite == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ if (!isSequential && onSeek == NULL) {
+ return DRWAV_FALSE; // <-- onSeek is required when in non-sequential mode.
+ }
+
+
+ // Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this.
+ if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
+ return DRWAV_FALSE;
+ }
+ if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return DRWAV_FALSE;
+ }
+
+
+ drwav_zero_memory(pWav, sizeof(*pWav));
+ pWav->onWrite = onWrite;
+ pWav->onSeek = onSeek;
+ pWav->pUserData = pUserData;
+ pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
+ pWav->fmt.channels = (drwav_uint16)pFormat->channels;
+ pWav->fmt.sampleRate = pFormat->sampleRate;
+ pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
+ pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
+ pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
+ pWav->fmt.extendedSize = 0;
+ pWav->isSequentialWrite = isSequential;
+
+
+ size_t runningPos = 0;
+
+ // The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In
+ // sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-
+ // sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.
+ drwav_uint64 initialDataChunkSize = 0;
+ if (isSequential) {
+ initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
+
+ // The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64
+ // so for the sake of simplicity I'm not doing any validation for that.
+ if (pFormat->container == drwav_container_riff) {
+ if (initialDataChunkSize > (0xFFFFFFFF - 36)) {
+ return DRWAV_FALSE; // Not enough room to store every sample.
+ }
+ }
+ }
+
+ pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
+
+
+ // "RIFF" chunk.
+ if (pFormat->container == drwav_container_riff) {
+ drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; // +36 = "RIFF"+[RIFF Chunk Size]+"WAVE" + [sizeof "fmt " chunk]
+ runningPos += pWav->onWrite(pUserData, "RIFF", 4);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 4);
+ runningPos += pWav->onWrite(pUserData, "WAVE", 4);
+ } else {
+ drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+ runningPos += pWav->onWrite(pUserData, drwavGUID_W64_RIFF, 16);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 8);
+ runningPos += pWav->onWrite(pUserData, drwavGUID_W64_WAVE, 16);
+ }
+
+ // "fmt " chunk.
+ drwav_uint64 chunkSizeFMT;
+ if (pFormat->container == drwav_container_riff) {
+ chunkSizeFMT = 16;
+ runningPos += pWav->onWrite(pUserData, "fmt ", 4);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 4);
+ } else {
+ chunkSizeFMT = 40;
+ runningPos += pWav->onWrite(pUserData, drwavGUID_W64_FMT, 16);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 8);
+ }
+
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.formatTag, 2);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.channels, 2);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.sampleRate, 4);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.avgBytesPerSec, 4);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.blockAlign, 2);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.bitsPerSample, 2);
+
+ pWav->dataChunkDataPos = runningPos;
+
+ // "data" chunk.
+ if (pFormat->container == drwav_container_riff) {
+ drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
+ runningPos += pWav->onWrite(pUserData, "data", 4);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 4);
+ } else {
+ drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+ runningPos += pWav->onWrite(pUserData, drwavGUID_W64_DATA, 16);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 8);
+ }
+
+
+ // Simple validation.
+ if (pFormat->container == drwav_container_riff) {
+ if (runningPos != 20 + chunkSizeFMT + 8) {
+ return DRWAV_FALSE;
+ }
+ } else {
+ if (runningPos != 40 + chunkSizeFMT + 24) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+
+ // Set some properties for the client's convenience.
+ pWav->container = pFormat->container;
+ pWav->channels = (drwav_uint16)pFormat->channels;
+ pWav->sampleRate = pFormat->sampleRate;
+ pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
+ pWav->bytesPerSample = (drwav_uint16)(pFormat->bitsPerSample >> 3);
+ pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
+
+ return DRWAV_TRUE;
+}
+
+
+drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+ return drwav_init_write__internal(pWav, pFormat, 0, DRWAV_FALSE, onWrite, onSeek, pUserData); // DRWAV_FALSE = Not Sequential
+}
+
+drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData)
+{
+ return drwav_init_write__internal(pWav, pFormat, totalSampleCount, DRWAV_TRUE, onWrite, NULL, pUserData); // DRWAV_TRUE = Sequential
+}
+
+void drwav_uninit(drwav* pWav)
+{
+ if (pWav == NULL) {
+ return;
+ }
+
+ // If the drwav object was opened in write mode we'll need to finalize a few things:
+ // - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.
+ // - Set the size of the "data" chunk.
+ if (pWav->onWrite != NULL) {
+ // Validation for sequential mode.
+ if (pWav->isSequentialWrite) {
+ drwav_assert(pWav->dataChunkDataSize == pWav->dataChunkDataSizeTargetWrite);
+ }
+
+ // Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding.
+ drwav_uint32 paddingSize = 0;
+ if (pWav->container == drwav_container_riff) {
+ paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 2);
+ } else {
+ paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 8);
+ }
+
+ if (paddingSize > 0) {
+ drwav_uint64 paddingData = 0;
+ pWav->onWrite(pWav->pUserData, &paddingData, paddingSize);
+ }
+
+
+ // Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need
+ // to do this when using non-sequential mode.
+ if (pWav->onSeek && !pWav->isSequentialWrite) {
+ if (pWav->container == drwav_container_riff) {
+ // The "RIFF" chunk size.
+ if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
+ drwav_uint32 riffChunkSize = drwav_riff_chunk_size_riff(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &riffChunkSize, 4);
+ }
+
+ // the "data" chunk size.
+ if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) {
+ drwav_uint32 dataChunkSize = drwav_data_chunk_size_riff(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &dataChunkSize, 4);
+ }
+ } else {
+ // The "RIFF" chunk size.
+ if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
+ drwav_uint64 riffChunkSize = drwav_riff_chunk_size_w64(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &riffChunkSize, 8);
+ }
+
+ // The "data" chunk size.
+ if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) {
+ drwav_uint64 dataChunkSize = drwav_data_chunk_size_w64(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &dataChunkSize, 8);
+ }
+ }
+ }
+ }
+
+#ifndef DR_WAV_NO_STDIO
+ // If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()
+ // was used by looking at the onRead and onSeek callbacks.
+ if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
+ fclose((FILE*)pWav->pUserData);
+ }
+#endif
+}
+
+
+drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
+{
+ return drwav_open_ex(onRead, onSeek, NULL, pUserData, NULL, 0);
+}
+
+drwav* drwav_open_ex(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags)
+{
+ drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav));
+ if (pWav == NULL) {
+ return NULL;
+ }
+
+ if (!drwav_init_ex(pWav, onRead, onSeek, onChunk, pReadSeekUserData, pChunkUserData, flags)) {
+ DRWAV_FREE(pWav);
+ return NULL;
+ }
+
+ return pWav;
+}
+
+
+drwav* drwav_open_write__internal(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+ drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav));
+ if (pWav == NULL) {
+ return NULL;
+ }
+
+ if (!drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, onWrite, onSeek, pUserData)) {
+ DRWAV_FREE(pWav);
+ return NULL;
+ }
+
+ return pWav;
+}
+
+drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+ return drwav_open_write__internal(pFormat, 0, DRWAV_FALSE, onWrite, onSeek, pUserData);
+}
+
+drwav* drwav_open_write_sequential(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData)
+{
+ return drwav_open_write__internal(pFormat, totalSampleCount, DRWAV_TRUE, onWrite, NULL, pUserData);
+}
+
+void drwav_close(drwav* pWav)
+{
+ drwav_uninit(pWav);
+ DRWAV_FREE(pWav);
+}
+
+
+size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
+{
+ if (pWav == NULL || bytesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ if (bytesToRead > pWav->bytesRemaining) {
+ bytesToRead = (size_t)pWav->bytesRemaining;
+ }
+
+ size_t bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
+
+ pWav->bytesRemaining -= bytesRead;
+ return bytesRead;
+}
+
+drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut)
+{
+ if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ // Cannot use this function for compressed formats.
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ return 0;
+ }
+
+ // Don't try to read more samples than can potentially fit in the output buffer.
+ if (samplesToRead * pWav->bytesPerSample > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / pWav->bytesPerSample;
+ }
+
+ size_t bytesRead = drwav_read_raw(pWav, (size_t)(samplesToRead * pWav->bytesPerSample), pBufferOut);
+ return bytesRead / pWav->bytesPerSample;
+}
+
+drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
+{
+ return drwav_read(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels;
+}
+
+drwav_bool32 drwav_seek_to_first_sample(drwav* pWav)
+{
+ if (pWav->onWrite != NULL) {
+ return DRWAV_FALSE; // No seeking in write mode.
+ }
+
+ if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
+ return DRWAV_FALSE;
+ }
+
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ pWav->compressed.iCurrentSample = 0;
+ }
+
+ pWav->bytesRemaining = pWav->dataChunkDataSize;
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample)
+{
+ // Seeking should be compatible with wave files > 2GB.
+
+ if (pWav->onWrite != NULL) {
+ return DRWAV_FALSE; // No seeking in write mode.
+ }
+
+ if (pWav == NULL || pWav->onSeek == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ // If there are no samples, just return DRWAV_TRUE without doing anything.
+ if (pWav->totalSampleCount == 0) {
+ return DRWAV_TRUE;
+ }
+
+ // Make sure the sample is clamped.
+ if (sample >= pWav->totalSampleCount) {
+ sample = pWav->totalSampleCount - 1;
+ }
+
+
+ // For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need
+ // to seek back to the start.
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ // TODO: This can be optimized.
+
+ // If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards,
+ // we first need to seek back to the start and then just do the same thing as a forward seek.
+ if (sample < pWav->compressed.iCurrentSample) {
+ if (!drwav_seek_to_first_sample(pWav)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+ if (sample > pWav->compressed.iCurrentSample) {
+ drwav_uint64 offset = sample - pWav->compressed.iCurrentSample;
+
+ drwav_int16 devnull[2048];
+ while (offset > 0) {
+ drwav_uint64 samplesToRead = offset;
+ if (samplesToRead > 2048) {
+ samplesToRead = 2048;
+ }
+
+ drwav_uint64 samplesRead = 0;
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ samplesRead = drwav_read_s16__msadpcm(pWav, samplesToRead, devnull);
+ } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ samplesRead = drwav_read_s16__ima(pWav, samplesToRead, devnull);
+ } else {
+ assert(DRWAV_FALSE); // If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here.
+ }
+
+ if (samplesRead != samplesToRead) {
+ return DRWAV_FALSE;
+ }
+
+ offset -= samplesRead;
+ }
+ }
+ } else {
+ drwav_uint64 totalSizeInBytes = pWav->totalSampleCount * pWav->bytesPerSample;
+ drwav_assert(totalSizeInBytes >= pWav->bytesRemaining);
+
+ drwav_uint64 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
+ drwav_uint64 targetBytePos = sample * pWav->bytesPerSample;
+
+ drwav_uint64 offset;
+ if (currentBytePos < targetBytePos) {
+ // Offset forwards.
+ offset = (targetBytePos - currentBytePos);
+ } else {
+ // Offset backwards.
+ if (!drwav_seek_to_first_sample(pWav)) {
+ return DRWAV_FALSE;
+ }
+ offset = targetBytePos;
+ }
+
+ while (offset > 0) {
+ int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
+ if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->bytesRemaining -= offset32;
+ offset -= offset32;
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)
+{
+ return drwav_seek_to_sample(pWav, targetFrameIndex * pWav->channels);
+}
+
+
+size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
+{
+ if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
+ return 0;
+ }
+
+ size_t bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
+ pWav->dataChunkDataSize += bytesWritten;
+
+ return bytesWritten;
+}
+
+drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData)
+{
+ if (pWav == NULL || samplesToWrite == 0 || pData == NULL) {
+ return 0;
+ }
+
+ drwav_uint64 bytesToWrite = ((samplesToWrite * pWav->bitsPerSample) / 8);
+ if (bytesToWrite > DRWAV_SIZE_MAX) {
+ return 0;
+ }
+
+ drwav_uint64 bytesWritten = 0;
+ const drwav_uint8* pRunningData = (const drwav_uint8*)pData;
+ while (bytesToWrite > 0) {
+ drwav_uint64 bytesToWriteThisIteration = bytesToWrite;
+ if (bytesToWriteThisIteration > DRWAV_SIZE_MAX) {
+ bytesToWriteThisIteration = DRWAV_SIZE_MAX;
+ }
+
+ size_t bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
+ if (bytesJustWritten == 0) {
+ break;
+ }
+
+ bytesToWrite -= bytesJustWritten;
+ bytesWritten += bytesJustWritten;
+ pRunningData += bytesJustWritten;
+ }
+
+ return (bytesWritten * 8) / pWav->bitsPerSample;
+}
+
+drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
+{
+ return drwav_write(pWav, framesToWrite * pWav->channels, pData) / pWav->channels;
+}
+
+
+
+drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_assert(pWav != NULL);
+ drwav_assert(samplesToRead > 0);
+ drwav_assert(pBufferOut != NULL);
+
+ // TODO: Lots of room for optimization here.
+
+ drwav_uint64 totalSamplesRead = 0;
+
+ while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+ // If there are no cached samples we need to load a new block.
+ if (pWav->msadpcm.cachedSampleCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
+ if (pWav->channels == 1) {
+ // Mono.
+ drwav_uint8 header[7];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalSamplesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->msadpcm.predictor[0] = header[0];
+ pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 1);
+ pWav->msadpcm.prevSamples[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 3);
+ pWav->msadpcm.prevSamples[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 5);
+ pWav->msadpcm.cachedSamples[2] = pWav->msadpcm.prevSamples[0][0];
+ pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.cachedSampleCount = 2;
+ } else {
+ // Stereo.
+ drwav_uint8 header[14];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalSamplesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->msadpcm.predictor[0] = header[0];
+ pWav->msadpcm.predictor[1] = header[1];
+ pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 2);
+ pWav->msadpcm.delta[1] = drwav__bytes_to_s16(header + 4);
+ pWav->msadpcm.prevSamples[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 6);
+ pWav->msadpcm.prevSamples[1][1] = (drwav_int32)drwav__bytes_to_s16(header + 8);
+ pWav->msadpcm.prevSamples[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 10);
+ pWav->msadpcm.prevSamples[1][0] = (drwav_int32)drwav__bytes_to_s16(header + 12);
+
+ pWav->msadpcm.cachedSamples[0] = pWav->msadpcm.prevSamples[0][0];
+ pWav->msadpcm.cachedSamples[1] = pWav->msadpcm.prevSamples[1][0];
+ pWav->msadpcm.cachedSamples[2] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[1][1];
+ pWav->msadpcm.cachedSampleCount = 4;
+ }
+ }
+
+ // Output anything that's cached.
+ while (samplesToRead > 0 && pWav->msadpcm.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+ pBufferOut[0] = (drwav_int16)pWav->msadpcm.cachedSamples[drwav_countof(pWav->msadpcm.cachedSamples) - pWav->msadpcm.cachedSampleCount];
+ pWav->msadpcm.cachedSampleCount -= 1;
+
+ pBufferOut += 1;
+ samplesToRead -= 1;
+ totalSamplesRead += 1;
+ pWav->compressed.iCurrentSample += 1;
+ }
+
+ if (samplesToRead == 0) {
+ return totalSamplesRead;
+ }
+
+
+ // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+ // loop iteration which will trigger the loading of a new block.
+ if (pWav->msadpcm.cachedSampleCount == 0) {
+ if (pWav->msadpcm.bytesRemainingInBlock == 0) {
+ continue;
+ } else {
+ drwav_uint8 nibbles;
+ if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
+ return totalSamplesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock -= 1;
+
+ // TODO: Optimize away these if statements.
+ drwav_int32 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
+ drwav_int32 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
+
+ static drwav_int32 adaptationTable[] = {
+ 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230
+ };
+ static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
+ static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
+
+ if (pWav->channels == 1) {
+ // Mono.
+ drwav_int32 newSample0;
+ newSample0 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample0 += nibble0 * pWav->msadpcm.delta[0];
+ newSample0 = drwav_clamp(newSample0, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.prevSamples[0][1] = newSample0;
+
+
+ drwav_int32 newSample1;
+ newSample1 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample1 += nibble1 * pWav->msadpcm.delta[0];
+ newSample1 = drwav_clamp(newSample1, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.prevSamples[0][1] = newSample1;
+
+
+ pWav->msadpcm.cachedSamples[2] = newSample0;
+ pWav->msadpcm.cachedSamples[3] = newSample1;
+ pWav->msadpcm.cachedSampleCount = 2;
+ } else {
+ // Stereo.
+
+ // Left.
+ drwav_int32 newSample0;
+ newSample0 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample0 += nibble0 * pWav->msadpcm.delta[0];
+ newSample0 = drwav_clamp(newSample0, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.prevSamples[0][1] = newSample0;
+
+
+ // Right.
+ drwav_int32 newSample1;
+ newSample1 = ((pWav->msadpcm.prevSamples[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevSamples[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
+ newSample1 += nibble1 * pWav->msadpcm.delta[1];
+ newSample1 = drwav_clamp(newSample1, -32768, 32767);
+
+ pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
+ if (pWav->msadpcm.delta[1] < 16) {
+ pWav->msadpcm.delta[1] = 16;
+ }
+
+ pWav->msadpcm.prevSamples[1][0] = pWav->msadpcm.prevSamples[1][1];
+ pWav->msadpcm.prevSamples[1][1] = newSample1;
+
+ pWav->msadpcm.cachedSamples[2] = newSample0;
+ pWav->msadpcm.cachedSamples[3] = newSample1;
+ pWav->msadpcm.cachedSampleCount = 2;
+ }
+ }
+ }
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_assert(pWav != NULL);
+ drwav_assert(samplesToRead > 0);
+ drwav_assert(pBufferOut != NULL);
+
+ // TODO: Lots of room for optimization here.
+
+ drwav_uint64 totalSamplesRead = 0;
+
+ while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+ // If there are no cached samples we need to load a new block.
+ if (pWav->ima.cachedSampleCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
+ if (pWav->channels == 1) {
+ // Mono.
+ drwav_uint8 header[4];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalSamplesRead;
+ }
+ pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);
+ pWav->ima.stepIndex[0] = header[2];
+ pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[0];
+ pWav->ima.cachedSampleCount = 1;
+ } else {
+ // Stereo.
+ drwav_uint8 header[8];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalSamplesRead;
+ }
+ pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);
+ pWav->ima.stepIndex[0] = header[2];
+ pWav->ima.predictor[1] = drwav__bytes_to_s16(header + 4);
+ pWav->ima.stepIndex[1] = header[6];
+
+ pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 2] = pWav->ima.predictor[0];
+ pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[1];
+ pWav->ima.cachedSampleCount = 2;
+ }
+ }
+
+ // Output anything that's cached.
+ while (samplesToRead > 0 && pWav->ima.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+ pBufferOut[0] = (drwav_int16)pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount];
+ pWav->ima.cachedSampleCount -= 1;
+
+ pBufferOut += 1;
+ samplesToRead -= 1;
+ totalSamplesRead += 1;
+ pWav->compressed.iCurrentSample += 1;
+ }
+
+ if (samplesToRead == 0) {
+ return totalSamplesRead;
+ }
+
+ // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+ // loop iteration which will trigger the loading of a new block.
+ if (pWav->ima.cachedSampleCount == 0) {
+ if (pWav->ima.bytesRemainingInBlock == 0) {
+ continue;
+ } else {
+ static drwav_int32 indexTable[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+ static drwav_int32 stepTable[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
+ };
+
+ // From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the
+ // left channel, 4 bytes for the right channel.
+ pWav->ima.cachedSampleCount = 8 * pWav->channels;
+ for (drwav_uint32 iChannel = 0; iChannel < pWav->channels; ++iChannel) {
+ drwav_uint8 nibbles[4];
+ if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
+ return totalSamplesRead;
+ }
+ pWav->ima.bytesRemainingInBlock -= 4;
+
+ for (drwav_uint32 iByte = 0; iByte < 4; ++iByte) {
+ drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
+ drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
+
+ drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
+ drwav_int32 predictor = pWav->ima.predictor[iChannel];
+
+ drwav_int32 diff = step >> 3;
+ if (nibble0 & 1) diff += step >> 2;
+ if (nibble0 & 2) diff += step >> 1;
+ if (nibble0 & 4) diff += step;
+ if (nibble0 & 8) diff = -diff;
+
+ predictor = drwav_clamp(predictor + diff, -32768, 32767);
+ pWav->ima.predictor[iChannel] = predictor;
+ pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
+ pWav->ima.cachedSamples[(drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
+
+
+ step = stepTable[pWav->ima.stepIndex[iChannel]];
+ predictor = pWav->ima.predictor[iChannel];
+
+ diff = step >> 3;
+ if (nibble1 & 1) diff += step >> 2;
+ if (nibble1 & 2) diff += step >> 1;
+ if (nibble1 & 4) diff += step;
+ if (nibble1 & 8) diff = -diff;
+
+ predictor = drwav_clamp(predictor + diff, -32768, 32767);
+ pWav->ima.predictor[iChannel] = predictor;
+ pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
+ pWav->ima.cachedSamples[(drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
+ }
+ }
+ }
+ }
+ }
+
+ return totalSamplesRead;
+}
+
+
+#ifndef DR_WAV_NO_CONVERSION_API
+static unsigned short g_drwavAlawTable[256] = {
+ 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
+ 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
+ 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
+ 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
+ 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
+ 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
+ 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
+ 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
+ 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
+ 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
+ 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
+ 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
+ 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
+ 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
+ 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
+ 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
+};
+
+static unsigned short g_drwavMulawTable[256] = {
+ 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
+ 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
+ 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
+ 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
+ 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
+ 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
+ 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
+ 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
+ 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
+ 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
+ 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
+ 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
+ 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
+ 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
+ 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
+ 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+};
+
+static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
+{
+ return (short)g_drwavAlawTable[sampleIn];
+}
+
+static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
+{
+ return (short)g_drwavMulawTable[sampleIn];
+}
+
+
+
+static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+ // Special case for 8-bit sample data because it's treated as unsigned.
+ if (bytesPerSample == 1) {
+ drwav_u8_to_s16(pOut, pIn, totalSampleCount);
+ return;
+ }
+
+
+ // Slightly more optimal implementation for common formats.
+ if (bytesPerSample == 2) {
+ for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ *pOut++ = ((const drwav_int16*)pIn)[i];
+ }
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_s16(pOut, pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
+ return;
+ }
+
+
+ // Anything more than 64 bits per sample is not supported.
+ if (bytesPerSample > 8) {
+ drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ // Generic, slow converter.
+ for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
+ }
+}
+
+static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
+ return;
+ } else {
+ // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+drwav_uint64 drwav_read_s16__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ // Fast path.
+ if (pWav->bytesPerSample == 2) {
+ return drwav_read(pWav, samplesToRead, pBufferOut);
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ // Don't try to read more samples than can potentially fit in the output buffer.
+ if (samplesToRead * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_s16__pcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_s16__msadpcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_s16__ieee(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_s16__alaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_s16__mulaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_s16__ima(pWav, samplesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ return drwav_read_s16(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels;
+}
+
+void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ int x = pIn[i];
+ r = x - 128;
+ r = r << 8;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ int x = ((int)(((unsigned int)(((const unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const unsigned char*)pIn)[i*3+2])) << 24)) >> 8;
+ r = x >> 8;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ int x = pIn[i];
+ r = x >> 16;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ float x = pIn[i];
+ float c;
+ c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
+ c = c + 1;
+ r = (int)(c * 32767.5f);
+ r = r - 32768;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ double x = pIn[i];
+ double c;
+ c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
+ c = c + 1;
+ r = (int)(c * 32767.5);
+ r = r - 32768;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ for (size_t i = 0; i < sampleCount; ++i) {
+ pOut[i] = drwav__alaw_to_s16(pIn[i]);
+ }
+}
+
+void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ for (size_t i = 0; i < sampleCount; ++i) {
+ pOut[i] = drwav__mulaw_to_s16(pIn[i]);
+ }
+}
+
+
+
+static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample)
+{
+ // Special case for 8-bit sample data because it's treated as unsigned.
+ if (bytesPerSample == 1) {
+ drwav_u8_to_f32(pOut, pIn, sampleCount);
+ return;
+ }
+
+ // Slightly more optimal implementation for common formats.
+ if (bytesPerSample == 2) {
+ drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_f32(pOut, pIn, sampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
+ return;
+ }
+
+
+ // Anything more than 64 bits per sample is not supported.
+ if (bytesPerSample > 8) {
+ drwav_zero_memory(pOut, sampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ // Generic, slow converter.
+ for (unsigned int i = 0; i < sampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
+ }
+}
+
+static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ for (unsigned int i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((const float*)pIn)[i];
+ }
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
+ return;
+ } else {
+ // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ drwav_zero_memory(pOut, sampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+
+drwav_uint64 drwav_read_f32__pcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+ pBufferOut += samplesRead;
+
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+ // want to duplicate that code.
+ drwav_uint64 totalSamplesRead = 0;
+ drwav_int16 samples16[2048];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__ima(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+ // want to duplicate that code.
+ drwav_uint64 totalSamplesRead = 0;
+ drwav_int16 samples16[2048];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__ieee(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ // Fast path.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bytesPerSample == 4) {
+ return drwav_read(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ // Don't try to read more samples than can potentially fit in the output buffer.
+ if (samplesToRead * sizeof(float) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(float);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_f32__pcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_f32__msadpcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_f32__ieee(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_f32__alaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_f32__mulaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_f32__ima(pWav, samplesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ return drwav_read_f32(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels;
+}
+
+void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+#ifdef DR_WAV_LIBSNDFILE_COMPAT
+ // It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
+ // libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
+ // the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated
+ // correctness testing. This is disabled by default.
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
+ }
+#else
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (pIn[i] / 255.0f) * 2 - 1;
+ }
+#endif
+}
+
+void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = pIn[i] / 32768.0f;
+ }
+}
+
+void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ unsigned int s0 = pIn[i*3 + 0];
+ unsigned int s1 = pIn[i*3 + 1];
+ unsigned int s2 = pIn[i*3 + 2];
+
+ int sample32 = (int)((s0 << 8) | (s1 << 16) | (s2 << 24));
+ *pOut++ = (float)(sample32 / 2147483648.0);
+ }
+}
+
+void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (float)(pIn[i] / 2147483648.0);
+ }
+}
+
+void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (float)pIn[i];
+ }
+}
+
+void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
+ }
+}
+
+void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
+ }
+}
+
+
+
+static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+ // Special case for 8-bit sample data because it's treated as unsigned.
+ if (bytesPerSample == 1) {
+ drwav_u8_to_s32(pOut, pIn, totalSampleCount);
+ return;
+ }
+
+ // Slightly more optimal implementation for common formats.
+ if (bytesPerSample == 2) {
+ drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_s32(pOut, pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ *pOut++ = ((const drwav_int32*)pIn)[i];
+ }
+ return;
+ }
+
+
+ // Anything more than 64 bits per sample is not supported.
+ if (bytesPerSample > 8) {
+ drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ // Generic, slow converter.
+ for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
+ }
+}
+
+static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
+ return;
+ } else {
+ // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+
+drwav_uint64 drwav_read_s32__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ // Fast path.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bytesPerSample == 4) {
+ return drwav_read(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+ // want to duplicate that code.
+ drwav_uint64 totalSamplesRead = 0;
+ drwav_int16 samples16[2048];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+ // want to duplicate that code.
+ drwav_uint64 totalSamplesRead = 0;
+ drwav_int16 samples16[2048];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ // Don't try to read more samples than can potentially fit in the output buffer.
+ if (samplesToRead * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32);
+ }
+
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_s32__pcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_s32__msadpcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_s32__ieee(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_s32__alaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_s32__mulaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_s32__ima(pWav, samplesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ return drwav_read_s32(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels;
+}
+
+void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((int)pIn[i] - 128) << 24;
+ }
+}
+
+void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = pIn[i] << 16;
+ }
+}
+
+void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ unsigned int s0 = pIn[i*3 + 0];
+ unsigned int s1 = pIn[i*3 + 1];
+ unsigned int s2 = pIn[i*3 + 2];
+
+ drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
+ *pOut++ = sample32;
+ }
+}
+
+void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+ }
+}
+
+void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+ }
+}
+
+void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
+ }
+}
+
+void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i= 0; i < sampleCount; ++i) {
+ *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
+ }
+}
+
+
+
+drwav_int16* drwav__read_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ drwav_assert(pWav != NULL);
+
+ drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int16);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; // File's too big.
+ }
+
+ drwav_int16* pSampleData = (drwav_int16*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; // Failed to allocate memory.
+ }
+
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ if (samplesRead != pWav->totalSampleCount) {
+ DRWAV_FREE(pSampleData);
+ drwav_uninit(pWav);
+ return NULL; // There was an error reading the samples.
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) *sampleRate = pWav->sampleRate;
+ if (channels) *channels = pWav->channels;
+ if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ return pSampleData;
+}
+
+float* drwav__read_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ drwav_assert(pWav != NULL);
+
+ drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(float);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; // File's too big.
+ }
+
+ float* pSampleData = (float*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; // Failed to allocate memory.
+ }
+
+ drwav_uint64 samplesRead = drwav_read_f32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ if (samplesRead != pWav->totalSampleCount) {
+ DRWAV_FREE(pSampleData);
+ drwav_uninit(pWav);
+ return NULL; // There was an error reading the samples.
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) *sampleRate = pWav->sampleRate;
+ if (channels) *channels = pWav->channels;
+ if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ return pSampleData;
+}
+
+drwav_int32* drwav__read_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ drwav_assert(pWav != NULL);
+
+ drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int32);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; // File's too big.
+ }
+
+ drwav_int32* pSampleData = (drwav_int32*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; // Failed to allocate memory.
+ }
+
+ drwav_uint64 samplesRead = drwav_read_s32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ if (samplesRead != pWav->totalSampleCount) {
+ DRWAV_FREE(pSampleData);
+ drwav_uninit(pWav);
+ return NULL; // There was an error reading the samples.
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) *sampleRate = pWav->sampleRate;
+ if (channels) *channels = pWav->channels;
+ if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ return pSampleData;
+}
+
+
+drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (channels) *channels = 0;
+ if (sampleRate) *sampleRate = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int16* result = drwav_open_and_read_s16(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+
+float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ float* result = drwav_open_and_read_f32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+
+drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int32* result = drwav_open_and_read_s32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+
+#ifndef DR_WAV_NO_STDIO
+drwav_int16* drwav_open_file_and_read_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_file(&wav, filename)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int16* result = drwav_open_file_and_read_s16(filename, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+
+float* drwav_open_file_and_read_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_file(&wav, filename)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ float* result = drwav_open_file_and_read_f32(filename, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+
+drwav_int32* drwav_open_file_and_read_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_file(&wav, filename)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int32* result = drwav_open_file_and_read_s32(filename, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+#endif
+
+drwav_int16* drwav_open_memory_and_read_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_memory(&wav, data, dataSize)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int16* result = drwav_open_memory_and_read_s16(data, dataSize, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+
+float* drwav_open_memory_and_read_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_memory(&wav, data, dataSize)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ float* result = drwav_open_memory_and_read_f32(data, dataSize, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+
+drwav_int32* drwav_open_memory_and_read_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_memory(&wav, data, dataSize)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut)
+{
+ if (channelsOut) *channelsOut = 0;
+ if (sampleRateOut) *sampleRateOut = 0;
+ if (totalFrameCountOut) *totalFrameCountOut = 0;
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalSampleCount;
+ drwav_int32* result = drwav_open_memory_and_read_s32(data, dataSize, &channels, &sampleRate, &totalSampleCount);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (channelsOut) *channelsOut = channels;
+ if (sampleRateOut) *sampleRateOut = sampleRate;
+ if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels;
+
+ return result;
+}
+#endif //DR_WAV_NO_CONVERSION_API
+
+
+void drwav_free(void* pDataReturnedByOpenAndRead)
+{
+ DRWAV_FREE(pDataReturnedByOpenAndRead);
+}
+
+#endif //DR_WAV_IMPLEMENTATION
+
+
+// REVISION HISTORY
+//
+// v0.9.0 - 2018-xx-xx
+// - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*().
+// - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*().
+// - Add built-in support for smpl chunks.
+// - Add support for firing a callback for each chunk in the file at initialization time.
+// - This is enabled through the drwav_init_ex(), etc. family of APIs.
+// - Add new reading APIs for reading by PCM frames instead of samples.
+//
+// v0.8.5 - 2018-09-11
+// - Const correctness.
+// - Fix a potential stack overflow.
+//
+// v0.8.4 - 2018-08-07
+// - Improve 64-bit detection.
+//
+// v0.8.3 - 2018-08-05
+// - Fix C++ build on older versions of GCC.
+//
+// v0.8.2 - 2018-08-02
+// - Fix some big-endian bugs.
+//
+// v0.8.1 - 2018-06-29
+// - Add support for sequential writing APIs.
+// - Disable seeking in write mode.
+// - Fix bugs with Wave64.
+// - Fix typos.
+//
+// v0.8 - 2018-04-27
+// - Bug fix.
+// - Start using major.minor.revision versioning.
+//
+// v0.7f - 2018-02-05
+// - Restrict ADPCM formats to a maximum of 2 channels.
+//
+// v0.7e - 2018-02-02
+// - Fix a crash.
+//
+// v0.7d - 2018-02-01
+// - Fix a crash.
+//
+// v0.7c - 2018-02-01
+// - Set drwav.bytesPerSample to 0 for all compressed formats.
+// - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for
+// all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).
+// - Fix some divide-by-zero errors.
+//
+// v0.7b - 2018-01-22
+// - Fix errors with seeking of compressed formats.
+// - Fix compilation error when DR_WAV_NO_CONVERSION_API
+//
+// v0.7a - 2017-11-17
+// - Fix some GCC warnings.
+//
+// v0.7 - 2017-11-04
+// - Add writing APIs.
+//
+// v0.6 - 2017-08-16
+// - API CHANGE: Rename dr_* types to drwav_*.
+// - Add support for custom implementations of malloc(), realloc(), etc.
+// - Add support for Microsoft ADPCM.
+// - Add support for IMA ADPCM (DVI, format code 0x11).
+// - Optimizations to drwav_read_s16().
+// - Bug fixes.
+//
+// v0.5g - 2017-07-16
+// - Change underlying type for booleans to unsigned.
+//
+// v0.5f - 2017-04-04
+// - Fix a minor bug with drwav_open_and_read_s16() and family.
+//
+// v0.5e - 2016-12-29
+// - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
+// - Minor fixes to documentation.
+//
+// v0.5d - 2016-12-28
+// - Use drwav_int*/drwav_uint* sized types to improve compiler support.
+//
+// v0.5c - 2016-11-11
+// - Properly handle JUNK chunks that come before the FMT chunk.
+//
+// v0.5b - 2016-10-23
+// - A minor change to drwav_bool8 and drwav_bool32 types.
+//
+// v0.5a - 2016-10-11
+// - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
+// - Improve A-law and mu-law efficiency.
+//
+// v0.5 - 2016-09-29
+// - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
+// keep it consistent with dr_audio and dr_flac.
+//
+// v0.4b - 2016-09-18
+// - Fixed a typo in documentation.
+//
+// v0.4a - 2016-09-18
+// - Fixed a typo.
+// - Change date format to ISO 8601 (YYYY-MM-DD)
+//
+// v0.4 - 2016-07-13
+// - API CHANGE. Make onSeek consistent with dr_flac.
+// - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.
+// - Added support for Sony Wave64.
+//
+// v0.3a - 2016-05-28
+// - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
+// - Fixed a memory leak.
+//
+// v0.3 - 2016-05-22
+// - Lots of API changes for consistency.
+//
+// v0.2a - 2016-05-16
+// - Fixed Linux/GCC build.
+//
+// v0.2 - 2016-05-11
+// - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.
+//
+// v0.1a - 2016-05-07
+// - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
+//
+// v0.1 - 2016-05-04
+// - Initial versioned release.
+
+
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+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 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.
+
+For more information, please refer to
+*/
diff --git a/src/soloud/src/audiosource/wav/soloud_wav.cpp b/src/soloud/src/audiosource/wav/soloud_wav.cpp
new file mode 100644
index 0000000..fd9b257
--- /dev/null
+++ b/src/soloud/src/audiosource/wav/soloud_wav.cpp
@@ -0,0 +1,396 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include
+#include "soloud.h"
+#include "soloud_wav.h"
+#include "soloud_file.h"
+#include "stb_vorbis.h"
+#include "dr_mp3.h"
+#include "dr_wav.h"
+#include "dr_flac.h"
+
+namespace SoLoud
+{
+ WavInstance::WavInstance(Wav *aParent)
+ {
+ mParent = aParent;
+ mOffset = 0;
+ }
+
+ unsigned int WavInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ if (mParent->mData == NULL)
+ return 0;
+
+ unsigned int dataleft = mParent->mSampleCount - mOffset;
+ unsigned int copylen = dataleft;
+ if (copylen > aSamplesToRead)
+ copylen = aSamplesToRead;
+
+ unsigned int i;
+ for (i = 0; i < mChannels; i++)
+ {
+ memcpy(aBuffer + i * aBufferSize, mParent->mData + mOffset + i * mParent->mSampleCount, sizeof(float) * copylen);
+ }
+
+ mOffset += copylen;
+ return copylen;
+ }
+
+ result WavInstance::rewind()
+ {
+ mOffset = 0;
+ mStreamPosition = 0.0f;
+ return 0;
+ }
+
+ bool WavInstance::hasEnded()
+ {
+ if (!(mFlags & AudioSourceInstance::LOOPING) && mOffset >= mParent->mSampleCount)
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ Wav::Wav()
+ {
+ mData = NULL;
+ mSampleCount = 0;
+ }
+
+ Wav::~Wav()
+ {
+ stop();
+ delete[] mData;
+ }
+
+#define MAKEDWORD(a,b,c,d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a))
+
+ result Wav::loadwav(MemoryFile *aReader)
+ {
+ drwav decoder;
+
+ if (!drwav_init_memory(&decoder, aReader->getMemPtr(), aReader->length()))
+ {
+ return FILE_LOAD_FAILED;
+ }
+
+ drwav_uint64 samples = decoder.totalSampleCount / decoder.channels;
+
+ if (!samples)
+ {
+ drwav_uninit(&decoder);
+ return FILE_LOAD_FAILED;
+ }
+
+ mData = new float[(unsigned int)(samples * decoder.channels)];
+ mBaseSamplerate = (float)decoder.sampleRate;
+ mSampleCount = (unsigned int)samples;
+ mChannels = decoder.channels;
+
+ unsigned int i, j, k;
+ for (i = 0; i < mSampleCount; i += 512)
+ {
+ float tmp[512 * MAX_CHANNELS];
+ unsigned int blockSize = (mSampleCount - i) > 512 ? 512 : mSampleCount - i;
+ drwav_read_pcm_frames_f32(&decoder, blockSize, tmp);
+ for (j = 0; j < blockSize; j++)
+ {
+ for (k = 0; k < decoder.channels; k++)
+ {
+ mData[k * mSampleCount + i + j] = tmp[j * decoder.channels + k];
+ }
+ }
+ }
+ drwav_uninit(&decoder);
+
+ return SO_NO_ERROR;
+ }
+
+ result Wav::loadogg(MemoryFile *aReader)
+ {
+ int e = 0;
+ stb_vorbis *vorbis = 0;
+ vorbis = stb_vorbis_open_memory(aReader->getMemPtr(), aReader->length(), &e, 0);
+
+ if (0 == vorbis)
+ {
+ return FILE_LOAD_FAILED;
+ }
+
+ stb_vorbis_info info = stb_vorbis_get_info(vorbis);
+ mBaseSamplerate = (float)info.sample_rate;
+ int samples = stb_vorbis_stream_length_in_samples(vorbis);
+
+ if (info.channels > MAX_CHANNELS)
+ {
+ mChannels = MAX_CHANNELS;
+ }
+ else
+ {
+ mChannels = info.channels;
+ }
+ mData = new float[samples * mChannels];
+ mSampleCount = samples;
+ samples = 0;
+ while(1)
+ {
+ float **outputs;
+ int n = stb_vorbis_get_frame_float(vorbis, NULL, &outputs);
+ if (n == 0)
+ {
+ break;
+ }
+
+ unsigned int ch;
+ for (ch = 0; ch < mChannels; ch++)
+ memcpy(mData + samples + mSampleCount * ch, outputs[ch], sizeof(float) * n);
+
+ samples += n;
+ }
+ stb_vorbis_close(vorbis);
+
+ return 0;
+ }
+
+ result Wav::loadmp3(MemoryFile *aReader)
+ {
+ drmp3 decoder;
+
+ if (!drmp3_init_memory(&decoder, aReader->getMemPtr(), aReader->length(), NULL))
+ {
+ return FILE_LOAD_FAILED;
+ }
+
+ drmp3_uint64 samples = drmp3_get_pcm_frame_count(&decoder);
+
+ if (!samples)
+ {
+ drmp3_uninit(&decoder);
+ return FILE_LOAD_FAILED;
+ }
+
+ mData = new float[(unsigned int)(samples * decoder.channels)];
+ mBaseSamplerate = (float)decoder.sampleRate;
+ mSampleCount = (unsigned int)samples;
+ mChannels = decoder.channels;
+ drmp3_seek_to_pcm_frame(&decoder, 0);
+
+ unsigned int i, j, k;
+ for (i = 0; i 512 ? 512 : mSampleCount - i;
+ drmp3_read_pcm_frames_f32(&decoder, blockSize, tmp);
+ for (j = 0; j < blockSize; j++)
+ {
+ for (k = 0; k < decoder.channels; k++)
+ {
+ mData[k * mSampleCount + i + j] = tmp[j * decoder.channels + k];
+ }
+ }
+ }
+ drmp3_uninit(&decoder);
+
+ return SO_NO_ERROR;
+ }
+
+ result Wav::loadflac(MemoryFile *aReader)
+ {
+ drflac *decoder = drflac_open_memory(aReader->mDataPtr, aReader->mDataLength);
+
+ if (!decoder)
+ {
+ return FILE_LOAD_FAILED;
+ }
+
+ drflac_uint64 samples = decoder->totalSampleCount;
+
+ if (!samples)
+ {
+ drflac_close(decoder);
+ return FILE_LOAD_FAILED;
+ }
+
+ mData = new float[(unsigned int)(samples * decoder->channels)];
+ mBaseSamplerate = (float)decoder->sampleRate;
+ mSampleCount = (unsigned int)samples;
+ mChannels = decoder->channels;
+ drflac_seek_to_pcm_frame(decoder, 0);
+
+ unsigned int i, j, k;
+ for (i = 0; i < mSampleCount; i += 512)
+ {
+ float tmp[512 * MAX_CHANNELS];
+ unsigned int blockSize = (mSampleCount - i) > 512 ? 512 : mSampleCount - i;
+ drflac_read_pcm_frames_f32(decoder, blockSize, tmp);
+ for (j = 0; j < blockSize; j++)
+ {
+ for (k = 0; k < decoder->channels; k++)
+ {
+ mData[k * mSampleCount + i + j] = tmp[j * decoder->channels + k];
+ }
+ }
+ }
+ drflac_close(decoder);
+
+ return SO_NO_ERROR;
+ }
+
+ result Wav::testAndLoadFile(MemoryFile *aReader)
+ {
+ delete[] mData;
+ mData = 0;
+ mSampleCount = 0;
+ mChannels = 1;
+ int tag = aReader->read32();
+ if (tag == MAKEDWORD('O','g','g','S'))
+ {
+ return loadogg(aReader);
+
+ }
+ else if (tag == MAKEDWORD('R','I','F','F'))
+ {
+ return loadwav(aReader);
+ }
+ else if (tag == MAKEDWORD('f', 'L', 'a', 'C'))
+ {
+ return loadflac(aReader);
+ }
+ else if (loadmp3(aReader) == SO_NO_ERROR)
+ {
+ return SO_NO_ERROR;
+ }
+
+ return FILE_LOAD_FAILED;
+ }
+
+ result Wav::load(const char *aFilename)
+ {
+ if (aFilename == 0)
+ return INVALID_PARAMETER;
+ stop();
+ DiskFile dr;
+ int res = dr.open(aFilename);
+ if (res == SO_NO_ERROR)
+ return loadFile(&dr);
+ return FILE_LOAD_FAILED;
+ }
+
+ result Wav::loadMem(unsigned char *aMem, unsigned int aLength, bool aCopy, bool aTakeOwnership)
+ {
+ if (aMem == NULL || aLength == 0)
+ return INVALID_PARAMETER;
+ stop();
+
+ MemoryFile dr;
+ dr.openMem(aMem, aLength, aCopy, aTakeOwnership);
+ return testAndLoadFile(&dr);
+ }
+
+ result Wav::loadFile(File *aFile)
+ {
+ if (!aFile)
+ return INVALID_PARAMETER;
+ stop();
+
+ MemoryFile mr;
+ result res = mr.openFileToMem(aFile);
+
+ if (res != SO_NO_ERROR)
+ {
+ return res;
+ }
+ return testAndLoadFile(&mr);
+ }
+
+ AudioSourceInstance *Wav::createInstance()
+ {
+ return new WavInstance(this);
+ }
+
+ double Wav::getLength()
+ {
+ if (mBaseSamplerate == 0)
+ return 0;
+ return mSampleCount / mBaseSamplerate;
+ }
+
+ result Wav::loadRawWave8(unsigned char *aMem, unsigned int aLength, float aSamplerate, unsigned int aChannels)
+ {
+ if (aMem == 0 || aLength == 0 || aSamplerate <= 0 || aChannels < 1)
+ return INVALID_PARAMETER;
+ stop();
+ delete[] mData;
+ mData = new float[aLength];
+ mSampleCount = aLength / aChannels;
+ mChannels = aChannels;
+ mBaseSamplerate = aSamplerate;
+ unsigned int i;
+ for (i = 0; i < aLength; i++)
+ mData[i] = ((signed)aMem[i] - 128) / (float)0x80;
+ return SO_NO_ERROR;
+ }
+
+ result Wav::loadRawWave16(short *aMem, unsigned int aLength, float aSamplerate, unsigned int aChannels)
+ {
+ if (aMem == 0 || aLength == 0 || aSamplerate <= 0 || aChannels < 1)
+ return INVALID_PARAMETER;
+ stop();
+ delete[] mData;
+ mData = new float[aLength];
+ mSampleCount = aLength / aChannels;
+ mChannels = aChannels;
+ mBaseSamplerate = aSamplerate;
+ unsigned int i;
+ for (i = 0; i < aLength; i++)
+ mData[i] = ((signed short)aMem[i]) / (float)0x8000;
+ return SO_NO_ERROR;
+ }
+
+ result Wav::loadRawWave(float *aMem, unsigned int aLength, float aSamplerate, unsigned int aChannels, bool aCopy, bool aTakeOwndership)
+ {
+ if (aMem == 0 || aLength == 0 || aSamplerate <= 0 || aChannels < 1)
+ return INVALID_PARAMETER;
+ stop();
+ delete[] mData;
+ if (aCopy == true || aTakeOwndership == false)
+ {
+ mData = new float[aLength];
+ memcpy(mData, aMem, sizeof(float) * aLength);
+ }
+ else
+ {
+ mData = aMem;
+ }
+ mSampleCount = aLength / aChannels;
+ mChannels = aChannels;
+ mBaseSamplerate = aSamplerate;
+ return SO_NO_ERROR;
+ }
+};
diff --git a/src/soloud/src/audiosource/wav/soloud_wavstream.cpp b/src/soloud/src/audiosource/wav/soloud_wavstream.cpp
new file mode 100644
index 0000000..bef887e
--- /dev/null
+++ b/src/soloud/src/audiosource/wav/soloud_wavstream.cpp
@@ -0,0 +1,658 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include
+#include "soloud.h"
+#include "dr_flac.h"
+#include "dr_mp3.h"
+#include "dr_wav.h"
+#include "soloud_wavstream.h"
+#include "soloud_file.h"
+#include "stb_vorbis.h"
+
+namespace SoLoud
+{
+ size_t drflac_read_func(void* pUserData, void* pBufferOut, size_t bytesToRead)
+ {
+ File *fp = (File*)pUserData;
+ return fp->read((unsigned char*)pBufferOut, (unsigned int)bytesToRead);
+ }
+
+ size_t drmp3_read_func(void* pUserData, void* pBufferOut, size_t bytesToRead)
+ {
+ File *fp = (File*)pUserData;
+ return fp->read((unsigned char*)pBufferOut, (unsigned int)bytesToRead);
+ }
+
+ size_t drwav_read_func(void* pUserData, void* pBufferOut, size_t bytesToRead)
+ {
+ File *fp = (File*)pUserData;
+ return fp->read((unsigned char*)pBufferOut, (unsigned int)bytesToRead);
+ }
+
+ drflac_bool32 drflac_seek_func(void* pUserData, int offset, drflac_seek_origin origin)
+ {
+ File *fp = (File*)pUserData;
+ if (origin != drflac_seek_origin_start)
+ offset += fp->pos();
+ fp->seek(offset);
+ return 1;
+ }
+
+ drmp3_bool32 drmp3_seek_func(void* pUserData, int offset, drmp3_seek_origin origin)
+ {
+ File *fp = (File*)pUserData;
+ if (origin != drmp3_seek_origin_start)
+ offset += fp->pos();
+ fp->seek(offset);
+ return 1;
+ }
+
+ drmp3_bool32 drwav_seek_func(void* pUserData, int offset, drwav_seek_origin origin)
+ {
+ File *fp = (File*)pUserData;
+ if (origin != drwav_seek_origin_start)
+ offset += fp->pos();
+ fp->seek(offset);
+ return 1;
+ }
+
+ WavStreamInstance::WavStreamInstance(WavStream *aParent)
+ {
+ mParent = aParent;
+ mOffset = 0;
+ mCodec.mOgg = 0;
+ mCodec.mFlac = 0;
+ mCodec.mMp3 = 0;
+ mCodec.mWav = 0;
+ mFile = 0;
+ if (aParent->mMemFile)
+ {
+ MemoryFile *mf = new MemoryFile();
+ mFile = mf;
+ mf->openMem(aParent->mMemFile->getMemPtr(), aParent->mMemFile->length(), false, false);
+ }
+ else
+ if (aParent->mFilename)
+ {
+ DiskFile *df = new DiskFile;
+ mFile = df;
+ df->open(aParent->mFilename);
+ }
+ else
+ if (aParent->mStreamFile)
+ {
+ mFile = aParent->mStreamFile;
+ mFile->seek(0); // stb_vorbis assumes file offset to be at start of ogg
+ }
+ else
+ {
+ return;
+ }
+
+ if (mFile)
+ {
+ if (mParent->mFiletype == WAVSTREAM_WAV)
+ {
+ mCodec.mWav = drwav_open(drwav_read_func, drwav_seek_func, (void*)mFile);
+ if (!mCodec.mWav)
+ {
+ if (mFile != mParent->mStreamFile)
+ delete mFile;
+ mFile = 0;
+ }
+ }
+ else
+ if (mParent->mFiletype == WAVSTREAM_OGG)
+ {
+ int e;
+
+ mCodec.mOgg = stb_vorbis_open_file((Soloud_Filehack *)mFile, 0, &e, 0);
+
+ if (!mCodec.mOgg)
+ {
+ if (mFile != mParent->mStreamFile)
+ delete mFile;
+ mFile = 0;
+ }
+ mOggFrameSize = 0;
+ mOggFrameOffset = 0;
+ mOggOutputs = 0;
+ }
+ else
+ if (mParent->mFiletype == WAVSTREAM_FLAC)
+ {
+ mCodec.mFlac = drflac_open(drflac_read_func, drflac_seek_func, (void*)mFile);
+ if (!mCodec.mFlac)
+ {
+ if (mFile != mParent->mStreamFile)
+ delete mFile;
+ mFile = 0;
+ }
+ }
+ else
+ if (mParent->mFiletype == WAVSTREAM_MP3)
+ {
+ mCodec.mMp3 = new drmp3;
+ if (!drmp3_init(mCodec.mMp3, drmp3_read_func, drmp3_seek_func, (void*)mFile, NULL))
+ {
+ delete mCodec.mMp3;
+ mCodec.mMp3 = 0;
+ if (mFile != mParent->mStreamFile)
+ delete mFile;
+ mFile = 0;
+ }
+ }
+ else
+ {
+ if (mFile != mParent->mStreamFile)
+ delete mFile;
+ mFile = NULL;
+ return;
+ }
+ }
+ }
+
+ WavStreamInstance::~WavStreamInstance()
+ {
+ switch (mParent->mFiletype)
+ {
+ case WAVSTREAM_OGG:
+ if (mCodec.mOgg)
+ {
+ stb_vorbis_close(mCodec.mOgg);
+ }
+ break;
+ case WAVSTREAM_FLAC:
+ if (mCodec.mFlac)
+ {
+ drflac_close(mCodec.mFlac);
+ }
+ break;
+ case WAVSTREAM_MP3:
+ if (mCodec.mMp3)
+ {
+ drmp3_uninit(mCodec.mMp3);
+ delete mCodec.mMp3;
+ }
+ break;
+ case WAVSTREAM_WAV:
+ if (mCodec.mWav)
+ {
+ drwav_close(mCodec.mWav);
+ }
+ }
+ if (mFile != mParent->mStreamFile)
+ {
+ delete mFile;
+ }
+ }
+
+ static int getOggData(float **aOggOutputs, float *aBuffer, int aSamples, int aPitch, int aFrameSize, int aFrameOffset, int aChannels)
+ {
+ if (aFrameSize <= 0)
+ return 0;
+
+ int samples = aSamples;
+ if (aFrameSize - aFrameOffset < samples)
+ {
+ samples = aFrameSize - aFrameOffset;
+ }
+
+ int i;
+ for (i = 0; i < aChannels; i++)
+ {
+ memcpy(aBuffer + aPitch * i, aOggOutputs[i] + aFrameOffset, sizeof(float) * samples);
+ }
+ return samples;
+ }
+
+
+
+ unsigned int WavStreamInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ unsigned int offset = 0;
+ if (mFile == NULL)
+ return 0;
+ switch (mParent->mFiletype)
+ {
+ case WAVSTREAM_FLAC:
+ {
+ unsigned int i, j, k;
+
+ for (i = 0; i < aSamplesToRead; i += 512)
+ {
+ float tmp[512 * MAX_CHANNELS];
+ unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
+ offset += (unsigned int)drflac_read_pcm_frames_f32(mCodec.mFlac, blockSize, tmp);
+
+ for (j = 0; j < blockSize; j++)
+ {
+ for (k = 0; k < mChannels; k++)
+ {
+ aBuffer[k * aSamplesToRead + i + j] = tmp[j * mCodec.mFlac->channels + k];
+ }
+ }
+ }
+ mOffset += offset;
+ return offset;
+ }
+ break;
+ case WAVSTREAM_MP3:
+ {
+ unsigned int i, j, k;
+
+ for (i = 0; i < aSamplesToRead; i += 512)
+ {
+ float tmp[512 * MAX_CHANNELS];
+ unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
+ offset += (unsigned int)drmp3_read_pcm_frames_f32(mCodec.mMp3, blockSize, tmp);
+
+ for (j = 0; j < blockSize; j++)
+ {
+ for (k = 0; k < mChannels; k++)
+ {
+ aBuffer[k * aSamplesToRead + i + j] = tmp[j * mCodec.mMp3->channels + k];
+ }
+ }
+ }
+ mOffset += offset;
+ return offset;
+ }
+ break;
+ case WAVSTREAM_OGG:
+ {
+ if (mOggFrameOffset < mOggFrameSize)
+ {
+ int b = getOggData(mOggOutputs, aBuffer, aSamplesToRead, aBufferSize, mOggFrameSize, mOggFrameOffset, mChannels);
+ mOffset += b;
+ offset += b;
+ mOggFrameOffset += b;
+ }
+
+ while (offset < aSamplesToRead)
+ {
+ mOggFrameSize = stb_vorbis_get_frame_float(mCodec.mOgg, NULL, &mOggOutputs);
+ mOggFrameOffset = 0;
+ int b = getOggData(mOggOutputs, aBuffer + offset, aSamplesToRead - offset, aBufferSize, mOggFrameSize, mOggFrameOffset, mChannels);
+ mOffset += b;
+ offset += b;
+ mOggFrameOffset += b;
+
+ if (mOffset >= mParent->mSampleCount || b == 0)
+ {
+ mOffset += offset;
+ return offset;
+ }
+ }
+ }
+ break;
+ case WAVSTREAM_WAV:
+ {
+ unsigned int i, j, k;
+
+ for (i = 0; i < aSamplesToRead; i += 512)
+ {
+ float tmp[512 * MAX_CHANNELS];
+ unsigned int blockSize = (aSamplesToRead - i) > 512 ? 512 : aSamplesToRead - i;
+ offset += (unsigned int)drwav_read_pcm_frames_f32(mCodec.mWav, blockSize, tmp);
+
+ for (j = 0; j < blockSize; j++)
+ {
+ for (k = 0; k < mChannels; k++)
+ {
+ aBuffer[k * aSamplesToRead + i + j] = tmp[j * mCodec.mWav->channels + k];
+ }
+ }
+ }
+ mOffset += offset;
+ return offset;
+ }
+ break;
+ }
+ return aSamplesToRead;
+ }
+
+ result WavStreamInstance::rewind()
+ {
+ switch (mParent->mFiletype)
+ {
+ case WAVSTREAM_OGG:
+ if (mCodec.mOgg)
+ {
+ stb_vorbis_seek_start(mCodec.mOgg);
+ }
+ break;
+ case WAVSTREAM_FLAC:
+ if (mCodec.mFlac)
+ {
+ drflac_seek_to_pcm_frame(mCodec.mFlac, 0);
+ }
+ break;
+ case WAVSTREAM_MP3:
+ if (mCodec.mMp3)
+ {
+ drmp3_seek_to_pcm_frame(mCodec.mMp3, 0);
+ }
+ break;
+ case WAVSTREAM_WAV:
+ if (mCodec.mWav)
+ {
+ drwav_seek_to_pcm_frame(mCodec.mWav, 0);
+ }
+ break;
+ }
+ mOffset = 0;
+ mStreamPosition = 0.0f;
+ return 0;
+ }
+
+ bool WavStreamInstance::hasEnded()
+ {
+ if (mOffset >= mParent->mSampleCount)
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ WavStream::WavStream()
+ {
+ mFilename = 0;
+ mSampleCount = 0;
+ mFiletype = WAVSTREAM_WAV;
+ mMemFile = 0;
+ mStreamFile = 0;
+ }
+
+ WavStream::~WavStream()
+ {
+ stop();
+ delete[] mFilename;
+ delete mMemFile;
+ }
+
+#define MAKEDWORD(a,b,c,d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a))
+
+ result WavStream::loadwav(File * fp)
+ {
+ fp->seek(0);
+ drwav* decoder = drwav_open(drwav_read_func, drwav_seek_func, (void*)fp);
+
+ if (decoder == NULL)
+ return FILE_LOAD_FAILED;
+
+ mChannels = decoder->channels;
+ if (mChannels > MAX_CHANNELS)
+ {
+ mChannels = MAX_CHANNELS;
+ }
+
+ mBaseSamplerate = (float)decoder->sampleRate;
+ mSampleCount = (unsigned int)decoder->totalSampleCount;
+ mFiletype = WAVSTREAM_WAV;
+ drwav_close(decoder);
+
+ return SO_NO_ERROR;
+ }
+
+ result WavStream::loadogg(File * fp)
+ {
+ fp->seek(0);
+ int e;
+ stb_vorbis *v;
+ v = stb_vorbis_open_file((Soloud_Filehack *)fp, 0, &e, 0);
+ if (v == NULL)
+ return FILE_LOAD_FAILED;
+ stb_vorbis_info info = stb_vorbis_get_info(v);
+ mChannels = info.channels;
+ if (info.channels > MAX_CHANNELS)
+ {
+ mChannels = MAX_CHANNELS;
+ }
+ mBaseSamplerate = (float)info.sample_rate;
+ int samples = stb_vorbis_stream_length_in_samples(v);
+ stb_vorbis_close(v);
+ mFiletype = WAVSTREAM_OGG;
+
+ mSampleCount = samples;
+
+ return 0;
+ }
+
+ result WavStream::loadflac(File * fp)
+ {
+ fp->seek(0);
+ drflac* decoder = drflac_open(drflac_read_func, drflac_seek_func, (void*)fp);
+
+ if (decoder == NULL)
+ return FILE_LOAD_FAILED;
+
+ mChannels = decoder->channels;
+ if (mChannels > MAX_CHANNELS)
+ {
+ mChannels = MAX_CHANNELS;
+ }
+
+ mBaseSamplerate = (float)decoder->sampleRate;
+ mSampleCount = (unsigned int)decoder->totalPCMFrameCount;
+ mFiletype = WAVSTREAM_FLAC;
+ drflac_close(decoder);
+
+ return SO_NO_ERROR;
+ }
+
+ result WavStream::loadmp3(File * fp)
+ {
+ fp->seek(0);
+ drmp3 decoder;
+ if (!drmp3_init(&decoder, drmp3_read_func, drmp3_seek_func, (void*)fp, NULL))
+ return FILE_LOAD_FAILED;
+
+
+ mChannels = decoder.channels;
+ if (mChannels > MAX_CHANNELS)
+ {
+ mChannels = MAX_CHANNELS;
+ }
+
+ drmp3_uint64 samples = drmp3_get_pcm_frame_count(&decoder);
+
+ mBaseSamplerate = (float)decoder.sampleRate;
+ mSampleCount = (unsigned int)samples;
+ mFiletype = WAVSTREAM_MP3;
+ drmp3_uninit(&decoder);
+
+ return SO_NO_ERROR;
+ }
+
+ result WavStream::load(const char *aFilename)
+ {
+ delete[] mFilename;
+ delete mMemFile;
+ mMemFile = 0;
+ mFilename = 0;
+ mSampleCount = 0;
+ DiskFile fp;
+ int res = fp.open(aFilename);
+ if (res != SO_NO_ERROR)
+ return res;
+
+ int len = (int)strlen(aFilename);
+ mFilename = new char[len+1];
+ memcpy(mFilename, aFilename, len);
+ mFilename[len] = 0;
+
+ res = parse(&fp);
+
+ if (res != SO_NO_ERROR)
+ {
+ delete[] mFilename;
+ mFilename = 0;
+ return res;
+ }
+
+ return 0;
+ }
+
+ result WavStream::loadMem(unsigned char *aData, unsigned int aDataLen, bool aCopy, bool aTakeOwnership)
+ {
+ delete[] mFilename;
+ delete mMemFile;
+ mStreamFile = 0;
+ mMemFile = 0;
+ mFilename = 0;
+ mSampleCount = 0;
+
+ if (aData == NULL || aDataLen == 0)
+ return INVALID_PARAMETER;
+
+ MemoryFile *mf = new MemoryFile();
+ int res = mf->openMem(aData, aDataLen, aCopy, aTakeOwnership);
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+
+ res = parse(mf);
+
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+
+ mMemFile = mf;
+
+ return 0;
+ }
+
+ result WavStream::loadToMem(const char *aFilename)
+ {
+ DiskFile df;
+ int res = df.open(aFilename);
+ if (res == SO_NO_ERROR)
+ {
+ res = loadFileToMem(&df);
+ }
+ return res;
+ }
+
+ result WavStream::loadFile(File *aFile)
+ {
+ delete[] mFilename;
+ delete mMemFile;
+ mStreamFile = 0;
+ mMemFile = 0;
+ mFilename = 0;
+ mSampleCount = 0;
+
+ int res = parse(aFile);
+
+ if (res != SO_NO_ERROR)
+ {
+ return res;
+ }
+
+ mStreamFile = aFile;
+
+ return 0;
+ }
+
+ result WavStream::loadFileToMem(File *aFile)
+ {
+ delete[] mFilename;
+ delete mMemFile;
+ mStreamFile = 0;
+ mMemFile = 0;
+ mFilename = 0;
+ mSampleCount = 0;
+
+ MemoryFile *mf = new MemoryFile();
+ int res = mf->openFileToMem(aFile);
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+
+ res = parse(mf);
+
+ if (res != SO_NO_ERROR)
+ {
+ delete mf;
+ return res;
+ }
+
+ mMemFile = mf;
+
+ return res;
+ }
+
+
+ result WavStream::parse(File *aFile)
+ {
+ int tag = aFile->read32();
+ int res = SO_NO_ERROR;
+ if (tag == MAKEDWORD('O', 'g', 'g', 'S'))
+ {
+ res = loadogg(aFile);
+ }
+ else
+ if (tag == MAKEDWORD('R', 'I', 'F', 'F'))
+ {
+ res = loadwav(aFile);
+ }
+ else
+ if (tag == MAKEDWORD('f', 'L', 'a', 'C'))
+ {
+ res = loadflac(aFile);
+ }
+ else
+ if (loadmp3(aFile) == SO_NO_ERROR)
+ {
+ res = SO_NO_ERROR;
+ }
+ else
+ {
+ res = FILE_LOAD_FAILED;
+ }
+ return res;
+ }
+
+ AudioSourceInstance *WavStream::createInstance()
+ {
+ return new WavStreamInstance(this);
+ }
+
+ double WavStream::getLength()
+ {
+ if (mBaseSamplerate == 0)
+ return 0;
+ return mSampleCount / mBaseSamplerate;
+ }
+};
diff --git a/src/soloud/src/audiosource/wav/stb_vorbis.c b/src/soloud/src/audiosource/wav/stb_vorbis.c
new file mode 100644
index 0000000..e7c92b1
--- /dev/null
+++ b/src/soloud/src/audiosource/wav/stb_vorbis.c
@@ -0,0 +1,5465 @@
+// Ogg Vorbis audio decoder - v1.14 - public domain
+// http://nothings.org/stb_vorbis/
+//
+// Original version written by Sean Barrett in 2007.
+//
+// Originally sponsored by RAD Game Tools. Seeking implementation
+// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
+// Elias Software, Aras Pranckevicius, and Sean Barrett.
+//
+// LICENSE
+//
+// See end of file for license information.
+//
+// Limitations:
+//
+// - floor 0 not supported (used in old ogg vorbis files pre-2004)
+// - lossless sample-truncation at beginning ignored
+// - cannot concatenate multiple vorbis streams
+// - sample positions are 32-bit, limiting seekable 192Khz
+// files to around 6 hours (Ogg supports 64-bit)
+//
+// Feature contributors:
+// Dougall Johnson (sample-exact seeking)
+//
+// Bugfix/warning contributors:
+// Terje Mathisen Niklas Frykholm Andy Hill
+// Casey Muratori John Bolton Gargaj
+// Laurent Gomila Marc LeBlanc Ronny Chevalier
+// Bernhard Wodo Evan Balster alxprd@github
+// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
+// Phillip Bennefall Rohit Thiago Goulart
+// manxorist@github saga musix github:infatum
+// Timur Gagiev
+//
+// Partial history:
+// 1.14 - 2018-02-11 - delete bogus dealloca usage
+// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
+// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+// 1.11 - 2017-07-23 - fix MinGW compilation
+// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version
+// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame
+// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const
+// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
+// some crash fixes when out of memory or with corrupt files
+// fix some inappropriately signed shifts
+// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
+// 1.04 - 2014-08-27 - fix missing const-correct case in API
+// 1.03 - 2014-08-07 - warning fixes
+// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows
+// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)
+// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
+// (API change) report sample rate for decode-full-file funcs
+//
+// See end of file for full version history.
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// HEADER BEGINS HERE
+//
+
+#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
+#define STB_VORBIS_INCLUDE_STB_VORBIS_H
+
+#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
+#define STB_VORBIS_NO_STDIO 1
+#endif
+
+#ifndef STB_VORBIS_NO_STDIO
+#include
+#endif
+
+#include "soloud_file_hack_on.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/////////// THREAD SAFETY
+
+// Individual stb_vorbis* handles are not thread-safe; you cannot decode from
+// them from multiple threads at the same time. However, you can have multiple
+// stb_vorbis* handles and decode from them independently in multiple thrads.
+
+
+/////////// MEMORY ALLOCATION
+
+// normally stb_vorbis uses malloc() to allocate memory at startup,
+// and alloca() to allocate temporary memory during a frame on the
+// stack. (Memory consumption will depend on the amount of setup
+// data in the file and how you set the compile flags for speed
+// vs. size. In my test files the maximal-size usage is ~150KB.)
+//
+// You can modify the wrapper functions in the source (setup_malloc,
+// setup_temp_malloc, temp_malloc) to change this behavior, or you
+// can use a simpler allocation model: you pass in a buffer from
+// which stb_vorbis will allocate _all_ its memory (including the
+// temp memory). "open" may fail with a VORBIS_outofmem if you
+// do not pass in enough data; there is no way to determine how
+// much you do need except to succeed (at which point you can
+// query get_info to find the exact amount required. yes I know
+// this is lame).
+//
+// If you pass in a non-NULL buffer of the type below, allocation
+// will occur from it as described above. Otherwise just pass NULL
+// to use malloc()/alloca()
+
+typedef struct
+{
+ char *alloc_buffer;
+ int alloc_buffer_length_in_bytes;
+} stb_vorbis_alloc;
+
+
+/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES
+
+typedef struct stb_vorbis stb_vorbis;
+
+typedef struct
+{
+ unsigned int sample_rate;
+ int channels;
+
+ unsigned int setup_memory_required;
+ unsigned int setup_temp_memory_required;
+ unsigned int temp_memory_required;
+
+ int max_frame_size;
+} stb_vorbis_info;
+
+// get general information about the file
+extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
+
+// get the last error detected (clears it, too)
+extern int stb_vorbis_get_error(stb_vorbis *f);
+
+// close an ogg vorbis file and free all memory in use
+extern void stb_vorbis_close(stb_vorbis *f);
+
+// this function returns the offset (in samples) from the beginning of the
+// file that will be returned by the next decode, if it is known, or -1
+// otherwise. after a flush_pushdata() call, this may take a while before
+// it becomes valid again.
+// NOT WORKING YET after a seek with PULLDATA API
+extern int stb_vorbis_get_sample_offset(stb_vorbis *f);
+
+// returns the current seek point within the file, or offset from the beginning
+// of the memory buffer. In pushdata mode it returns 0.
+extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
+
+/////////// PUSHDATA API
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+
+// this API allows you to get blocks of data from any source and hand
+// them to stb_vorbis. you have to buffer them; stb_vorbis will tell
+// you how much it used, and you have to give it the rest next time;
+// and stb_vorbis may not have enough data to work with and you will
+// need to give it the same data again PLUS more. Note that the Vorbis
+// specification does not bound the size of an individual frame.
+
+extern stb_vorbis *stb_vorbis_open_pushdata(
+ const unsigned char * datablock, int datablock_length_in_bytes,
+ int *datablock_memory_consumed_in_bytes,
+ int *error,
+ const stb_vorbis_alloc *alloc_buffer);
+// create a vorbis decoder by passing in the initial data block containing
+// the ogg&vorbis headers (you don't need to do parse them, just provide
+// the first N bytes of the file--you're told if it's not enough, see below)
+// on success, returns an stb_vorbis *, does not set error, returns the amount of
+// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes;
+// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed
+// if returns NULL and *error is VORBIS_need_more_data, then the input block was
+// incomplete and you need to pass in a larger block from the start of the file
+
+extern int stb_vorbis_decode_frame_pushdata(
+ stb_vorbis *f,
+ const unsigned char *datablock, int datablock_length_in_bytes,
+ int *channels, // place to write number of float * buffers
+ float ***output, // place to write float ** array of float * buffers
+ int *samples // place to write number of output samples
+ );
+// decode a frame of audio sample data if possible from the passed-in data block
+//
+// return value: number of bytes we used from datablock
+//
+// possible cases:
+// 0 bytes used, 0 samples output (need more data)
+// N bytes used, 0 samples output (resynching the stream, keep going)
+// N bytes used, M samples output (one frame of data)
+// note that after opening a file, you will ALWAYS get one N-bytes,0-sample
+// frame, because Vorbis always "discards" the first frame.
+//
+// Note that on resynch, stb_vorbis will rarely consume all of the buffer,
+// instead only datablock_length_in_bytes-3 or less. This is because it wants
+// to avoid missing parts of a page header if they cross a datablock boundary,
+// without writing state-machiney code to record a partial detection.
+//
+// The number of channels returned are stored in *channels (which can be
+// NULL--it is always the same as the number of channels reported by
+// get_info). *output will contain an array of float* buffers, one per
+// channel. In other words, (*output)[0][0] contains the first sample from
+// the first channel, and (*output)[1][0] contains the first sample from
+// the second channel.
+
+extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
+// inform stb_vorbis that your next datablock will not be contiguous with
+// previous ones (e.g. you've seeked in the data); future attempts to decode
+// frames will cause stb_vorbis to resynchronize (as noted above), and
+// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it
+// will begin decoding the _next_ frame.
+//
+// if you want to seek using pushdata, you need to seek in your file, then
+// call stb_vorbis_flush_pushdata(), then start calling decoding, then once
+// decoding is returning you data, call stb_vorbis_get_sample_offset, and
+// if you don't like the result, seek your file again and repeat.
+#endif
+
+
+////////// PULLING INPUT API
+
+#ifndef STB_VORBIS_NO_PULLDATA_API
+// This API assumes stb_vorbis is allowed to pull data from a source--
+// either a block of memory containing the _entire_ vorbis stream, or a
+// FILE * that you or it create, or possibly some other reading mechanism
+// if you go modify the source to replace the FILE * case with some kind
+// of callback to your code. (But if you don't support seeking, you may
+// just want to go ahead and use pushdata.)
+
+#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
+extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);
+#endif
+#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
+extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);
+#endif
+// decode an entire file and output the data interleaved into a malloc()ed
+// buffer stored in *output. The return value is the number of samples
+// decoded, or -1 if the file could not be opened or was not an ogg vorbis file.
+// When you're done with it, just free() the pointer returned in *output.
+
+extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
+ int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from an ogg vorbis stream in memory (note
+// this must be the entire stream!). on failure, returns NULL and sets *error
+
+#ifndef STB_VORBIS_NO_STDIO
+extern stb_vorbis * stb_vorbis_open_filename(const char *filename,
+ int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from a filename via fopen(). on failure,
+// returns NULL and sets *error (possibly to VORBIS_file_open_failure).
+
+extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
+ int *error, const stb_vorbis_alloc *alloc_buffer);
+// create an ogg vorbis decoder from an open FILE *, looking for a stream at
+// the _current_ seek point (ftell). on failure, returns NULL and sets *error.
+// note that stb_vorbis must "own" this stream; if you seek it in between
+// calls to stb_vorbis, it will become confused. Morever, if you attempt to
+// perform stb_vorbis_seek_*() operations on this file, it will assume it
+// owns the _entire_ rest of the file after the start point. Use the next
+// function, stb_vorbis_open_file_section(), to limit it.
+
+extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close,
+ int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);
+// create an ogg vorbis decoder from an open FILE *, looking for a stream at
+// the _current_ seek point (ftell); the stream will be of length 'len' bytes.
+// on failure, returns NULL and sets *error. note that stb_vorbis must "own"
+// this stream; if you seek it in between calls to stb_vorbis, it will become
+// confused.
+#endif
+
+extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
+extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
+// these functions seek in the Vorbis file to (approximately) 'sample_number'.
+// after calling seek_frame(), the next call to get_frame_*() will include
+// the specified sample. after calling stb_vorbis_seek(), the next call to
+// stb_vorbis_get_samples_* will start with the specified sample. If you
+// do not need to seek to EXACTLY the target sample when using get_samples_*,
+// you can also use seek_frame().
+
+extern int stb_vorbis_seek_start(stb_vorbis *f);
+// this function is equivalent to stb_vorbis_seek(f,0)
+
+extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
+extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
+// these functions return the total length of the vorbis stream
+
+extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);
+// decode the next frame and return the number of samples. the number of
+// channels returned are stored in *channels (which can be NULL--it is always
+// the same as the number of channels reported by get_info). *output will
+// contain an array of float* buffers, one per channel. These outputs will
+// be overwritten on the next call to stb_vorbis_get_frame_*.
+//
+// You generally should not intermix calls to stb_vorbis_get_frame_*()
+// and stb_vorbis_get_samples_*(), since the latter calls the former.
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
+extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples);
+#endif
+// decode the next frame and return the number of *samples* per channel.
+// Note that for interleaved data, you pass in the number of shorts (the
+// size of your array), but the return value is the number of samples per
+// channel, not the total number of samples.
+//
+// The data is coerced to the number of channels you request according to the
+// channel coercion rules (see below). You must pass in the size of your
+// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.
+// The maximum buffer size needed can be gotten from get_info(); however,
+// the Vorbis I specification implies an absolute maximum of 4096 samples
+// per channel.
+
+// Channel coercion rules:
+// Let M be the number of channels requested, and N the number of channels present,
+// and Cn be the nth channel; let stereo L be the sum of all L and center channels,
+// and stereo R be the sum of all R and center channels (channel assignment from the
+// vorbis spec).
+// M N output
+// 1 k sum(Ck) for all k
+// 2 * stereo L, stereo R
+// k l k > l, the first l channels, then 0s
+// k l k <= l, the first k channels
+// Note that this is not _good_ surround etc. mixing at all! It's just so
+// you get something useful.
+
+extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);
+extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);
+// gets num_samples samples, not necessarily on a frame boundary--this requires
+// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES.
+// Returns the number of samples stored per channel; it may be less than requested
+// at the end of the file. If there are no more samples in the file, returns 0.
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);
+extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);
+#endif
+// gets num_samples samples, not necessarily on a frame boundary--this requires
+// buffering so you have to supply the buffers. Applies the coercion rules above
+// to produce 'channels' channels. Returns the number of samples stored per channel;
+// it may be less than requested at the end of the file. If there are no more
+// samples in the file, returns 0.
+
+#endif
+
+//////// ERROR CODES
+
+enum STBVorbisError
+{
+ VORBIS__no_error,
+
+ VORBIS_need_more_data=1, // not a real error
+
+ VORBIS_invalid_api_mixing, // can't mix API modes
+ VORBIS_outofmem, // not enough memory
+ VORBIS_feature_not_supported, // uses floor 0
+ VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small
+ VORBIS_file_open_failure, // fopen() failed
+ VORBIS_seek_without_length, // can't seek in unknown-length file
+
+ VORBIS_unexpected_eof=10, // file is truncated?
+ VORBIS_seek_invalid, // seek past EOF
+
+ // decoding errors (corrupt/invalid stream) -- you probably
+ // don't care about the exact details of these
+
+ // vorbis errors:
+ VORBIS_invalid_setup=20,
+ VORBIS_invalid_stream,
+
+ // ogg errors:
+ VORBIS_missing_capture_pattern=30,
+ VORBIS_invalid_stream_structure_version,
+ VORBIS_continued_packet_flag_invalid,
+ VORBIS_incorrect_stream_serial_number,
+ VORBIS_invalid_first_page,
+ VORBIS_bad_packet_type,
+ VORBIS_cant_find_last_page,
+ VORBIS_seek_failed
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
+//
+// HEADER ENDS HERE
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef STB_VORBIS_HEADER_ONLY
+
+// global configuration settings (e.g. set these in the project/makefile),
+// or just set them in this file at the top (although ideally the first few
+// should be visible when the header file is compiled too, although it's not
+// crucial)
+
+// STB_VORBIS_NO_PUSHDATA_API
+// does not compile the code for the various stb_vorbis_*_pushdata()
+// functions
+// #define STB_VORBIS_NO_PUSHDATA_API
+
+// STB_VORBIS_NO_PULLDATA_API
+// does not compile the code for the non-pushdata APIs
+// #define STB_VORBIS_NO_PULLDATA_API
+
+// STB_VORBIS_NO_STDIO
+// does not compile the code for the APIs that use FILE *s internally
+// or externally (implied by STB_VORBIS_NO_PULLDATA_API)
+// #define STB_VORBIS_NO_STDIO
+
+// STB_VORBIS_NO_INTEGER_CONVERSION
+// does not compile the code for converting audio sample data from
+// float to integer (implied by STB_VORBIS_NO_PULLDATA_API)
+// #define STB_VORBIS_NO_INTEGER_CONVERSION
+
+// STB_VORBIS_NO_FAST_SCALED_FLOAT
+// does not use a fast float-to-int trick to accelerate float-to-int on
+// most platforms which requires endianness be defined correctly.
+//#define STB_VORBIS_NO_FAST_SCALED_FLOAT
+
+
+// STB_VORBIS_MAX_CHANNELS [number]
+// globally define this to the maximum number of channels you need.
+// The spec does not put a restriction on channels except that
+// the count is stored in a byte, so 255 is the hard limit.
+// Reducing this saves about 16 bytes per value, so using 16 saves
+// (255-16)*16 or around 4KB. Plus anything other memory usage
+// I forgot to account for. Can probably go as low as 8 (7.1 audio),
+// 6 (5.1 audio), or 2 (stereo only).
+#ifndef STB_VORBIS_MAX_CHANNELS
+#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone?
+#endif
+
+// STB_VORBIS_PUSHDATA_CRC_COUNT [number]
+// after a flush_pushdata(), stb_vorbis begins scanning for the
+// next valid page, without backtracking. when it finds something
+// that looks like a page, it streams through it and verifies its
+// CRC32. Should that validation fail, it keeps scanning. But it's
+// possible that _while_ streaming through to check the CRC32 of
+// one candidate page, it sees another candidate page. This #define
+// determines how many "overlapping" candidate pages it can search
+// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas
+// garbage pages could be as big as 64KB, but probably average ~16KB.
+// So don't hose ourselves by scanning an apparent 64KB page and
+// missing a ton of real ones in the interim; so minimum of 2
+#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT
+#define STB_VORBIS_PUSHDATA_CRC_COUNT 4
+#endif
+
+// STB_VORBIS_FAST_HUFFMAN_LENGTH [number]
+// sets the log size of the huffman-acceleration table. Maximum
+// supported value is 24. with larger numbers, more decodings are O(1),
+// but the table size is larger so worse cache missing, so you'll have
+// to probe (and try multiple ogg vorbis files) to find the sweet spot.
+#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH
+#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10
+#endif
+
+// STB_VORBIS_FAST_BINARY_LENGTH [number]
+// sets the log size of the binary-search acceleration table. this
+// is used in similar fashion to the fast-huffman size to set initial
+// parameters for the binary search
+
+// STB_VORBIS_FAST_HUFFMAN_INT
+// The fast huffman tables are much more efficient if they can be
+// stored as 16-bit results instead of 32-bit results. This restricts
+// the codebooks to having only 65535 possible outcomes, though.
+// (At least, accelerated by the huffman table.)
+#ifndef STB_VORBIS_FAST_HUFFMAN_INT
+#define STB_VORBIS_FAST_HUFFMAN_SHORT
+#endif
+
+// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls
+// back on binary searching for the correct one. This requires storing
+// extra tables with the huffman codes in sorted order. Defining this
+// symbol trades off space for speed by forcing a linear search in the
+// non-fast case, except for "sparse" codebooks.
+// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+
+// STB_VORBIS_DIVIDES_IN_RESIDUE
+// stb_vorbis precomputes the result of the scalar residue decoding
+// that would otherwise require a divide per chunk. you can trade off
+// space for time by defining this symbol.
+// #define STB_VORBIS_DIVIDES_IN_RESIDUE
+
+// STB_VORBIS_DIVIDES_IN_CODEBOOK
+// vorbis VQ codebooks can be encoded two ways: with every case explicitly
+// stored, or with all elements being chosen from a small range of values,
+// and all values possible in all elements. By default, stb_vorbis expands
+// this latter kind out to look like the former kind for ease of decoding,
+// because otherwise an integer divide-per-vector-element is required to
+// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can
+// trade off storage for speed.
+//#define STB_VORBIS_DIVIDES_IN_CODEBOOK
+
+#ifdef STB_VORBIS_CODEBOOK_SHORTS
+#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats"
+#endif
+
+// STB_VORBIS_DIVIDE_TABLE
+// this replaces small integer divides in the floor decode loop with
+// table lookups. made less than 1% difference, so disabled by default.
+
+// STB_VORBIS_NO_INLINE_DECODE
+// disables the inlining of the scalar codebook fast-huffman decode.
+// might save a little codespace; useful for debugging
+// #define STB_VORBIS_NO_INLINE_DECODE
+
+// STB_VORBIS_NO_DEFER_FLOOR
+// Normally we only decode the floor without synthesizing the actual
+// full curve. We can instead synthesize the curve immediately. This
+// requires more memory and is very likely slower, so I don't think
+// you'd ever want to do it except for debugging.
+// #define STB_VORBIS_NO_DEFER_FLOOR
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef STB_VORBIS_NO_PULLDATA_API
+ #define STB_VORBIS_NO_INTEGER_CONVERSION
+ #define STB_VORBIS_NO_STDIO
+#endif
+
+#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
+ #define STB_VORBIS_NO_STDIO 1
+#endif
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
+
+ // only need endianness for fast-float-to-int, which we don't
+ // use for pushdata
+
+ #ifndef STB_VORBIS_BIG_ENDIAN
+ #define STB_VORBIS_ENDIAN 0
+ #else
+ #define STB_VORBIS_ENDIAN 1
+ #endif
+
+#endif
+#endif
+
+
+#ifndef STB_VORBIS_NO_STDIO
+#include
+#endif
+
+#ifndef STB_VORBIS_NO_CRT
+ #include
+ #include
+ #include
+ #include
+
+ // find definition of alloca if it's not in stdlib.h:
+ #if defined(_MSC_VER) || defined(__MINGW32__)
+ #include
+ #endif
+ #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__)
+ #include
+ #endif
+#else // STB_VORBIS_NO_CRT
+ #define NULL 0
+ #define malloc(s) 0
+ #define free(s) ((void) 0)
+ #define realloc(s) 0
+#endif // STB_VORBIS_NO_CRT
+
+#include
+
+#ifdef __MINGW32__
+ // eff you mingw:
+ // "fixed":
+ // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/
+ // "no that broke the build, reverted, who cares about C":
+ // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/
+ #ifdef __forceinline
+ #undef __forceinline
+ #endif
+ #define __forceinline
+ #define alloca __builtin_alloca
+#elif !defined(_MSC_VER)
+ #if __GNUC__
+ #define __forceinline inline
+ #else
+ #define __forceinline
+ #endif
+#endif
+
+#if STB_VORBIS_MAX_CHANNELS > 256
+#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range"
+#endif
+
+#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24
+#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range"
+#endif
+
+
+#if 0
+#include
+#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1])
+#else
+#define CHECK(f) ((void) 0)
+#endif
+
+#define MAX_BLOCKSIZE_LOG 13 // from specification
+#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG)
+
+
+typedef unsigned char uint8;
+typedef signed char int8;
+typedef unsigned short uint16;
+typedef signed short int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+typedef float codetype;
+
+// @NOTE
+//
+// Some arrays below are tagged "//varies", which means it's actually
+// a variable-sized piece of data, but rather than malloc I assume it's
+// small enough it's better to just allocate it all together with the
+// main thing
+//
+// Most of the variables are specified with the smallest size I could pack
+// them into. It might give better performance to make them all full-sized
+// integers. It should be safe to freely rearrange the structures or change
+// the sizes larger--nothing relies on silently truncating etc., nor the
+// order of variables.
+
+#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH)
+#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1)
+
+typedef struct
+{
+ int dimensions, entries;
+ uint8 *codeword_lengths;
+ float minimum_value;
+ float delta_value;
+ uint8 value_bits;
+ uint8 lookup_type;
+ uint8 sequence_p;
+ uint8 sparse;
+ uint32 lookup_values;
+ codetype *multiplicands;
+ uint32 *codewords;
+ #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT
+ int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE];
+ #else
+ int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE];
+ #endif
+ uint32 *sorted_codewords;
+ int *sorted_values;
+ int sorted_entries;
+} Codebook;
+
+typedef struct
+{
+ uint8 order;
+ uint16 rate;
+ uint16 bark_map_size;
+ uint8 amplitude_bits;
+ uint8 amplitude_offset;
+ uint8 number_of_books;
+ uint8 book_list[16]; // varies
+} Floor0;
+
+typedef struct
+{
+ uint8 partitions;
+ uint8 partition_class_list[32]; // varies
+ uint8 class_dimensions[16]; // varies
+ uint8 class_subclasses[16]; // varies
+ uint8 class_masterbooks[16]; // varies
+ int16 subclass_books[16][8]; // varies
+ uint16 Xlist[31*8+2]; // varies
+ uint8 sorted_order[31*8+2];
+ uint8 neighbors[31*8+2][2];
+ uint8 floor1_multiplier;
+ uint8 rangebits;
+ int values;
+} Floor1;
+
+typedef union
+{
+ Floor0 floor0;
+ Floor1 floor1;
+} Floor;
+
+typedef struct
+{
+ uint32 begin, end;
+ uint32 part_size;
+ uint8 classifications;
+ uint8 classbook;
+ uint8 **classdata;
+ int16 (*residue_books)[8];
+} Residue;
+
+typedef struct
+{
+ uint8 magnitude;
+ uint8 angle;
+ uint8 mux;
+} MappingChannel;
+
+typedef struct
+{
+ uint16 coupling_steps;
+ MappingChannel *chan;
+ uint8 submaps;
+ uint8 submap_floor[15]; // varies
+ uint8 submap_residue[15]; // varies
+} Mapping;
+
+typedef struct
+{
+ uint8 blockflag;
+ uint8 mapping;
+ uint16 windowtype;
+ uint16 transformtype;
+} Mode;
+
+typedef struct
+{
+ uint32 goal_crc; // expected crc if match
+ int bytes_left; // bytes left in packet
+ uint32 crc_so_far; // running crc
+ int bytes_done; // bytes processed in _current_ chunk
+ uint32 sample_loc; // granule pos encoded in page
+} CRCscan;
+
+typedef struct
+{
+ uint32 page_start, page_end;
+ uint32 last_decoded_sample;
+} ProbedPage;
+
+struct stb_vorbis
+{
+ // user-accessible info
+ unsigned int sample_rate;
+ int channels;
+
+ unsigned int setup_memory_required;
+ unsigned int temp_memory_required;
+ unsigned int setup_temp_memory_required;
+
+ // input config
+#ifndef STB_VORBIS_NO_STDIO
+ FILE *f;
+ uint32 f_start;
+ int close_on_free;
+#endif
+
+ uint8 *stream;
+ uint8 *stream_start;
+ uint8 *stream_end;
+
+ uint32 stream_len;
+
+ uint8 push_mode;
+
+ uint32 first_audio_page_offset;
+
+ ProbedPage p_first, p_last;
+
+ // memory management
+ stb_vorbis_alloc alloc;
+ int setup_offset;
+ int temp_offset;
+
+ // run-time results
+ int eof;
+ enum STBVorbisError error;
+
+ // user-useful data
+
+ // header info
+ int blocksize[2];
+ int blocksize_0, blocksize_1;
+ int codebook_count;
+ Codebook *codebooks;
+ int floor_count;
+ uint16 floor_types[64]; // varies
+ Floor *floor_config;
+ int residue_count;
+ uint16 residue_types[64]; // varies
+ Residue *residue_config;
+ int mapping_count;
+ Mapping *mapping;
+ int mode_count;
+ Mode mode_config[64]; // varies
+
+ uint32 total_samples;
+
+ // decode buffer
+ float *channel_buffers[STB_VORBIS_MAX_CHANNELS];
+ float *outputs [STB_VORBIS_MAX_CHANNELS];
+
+ float *previous_window[STB_VORBIS_MAX_CHANNELS];
+ int previous_length;
+
+ #ifndef STB_VORBIS_NO_DEFER_FLOOR
+ int16 *finalY[STB_VORBIS_MAX_CHANNELS];
+ #else
+ float *floor_buffers[STB_VORBIS_MAX_CHANNELS];
+ #endif
+
+ uint32 current_loc; // sample location of next frame to decode
+ int current_loc_valid;
+
+ // per-blocksize precomputed data
+
+ // twiddle factors
+ float *A[2],*B[2],*C[2];
+ float *window[2];
+ uint16 *bit_reverse[2];
+
+ // current page/packet/segment streaming info
+ uint32 serial; // stream serial number for verification
+ int last_page;
+ int segment_count;
+ uint8 segments[255];
+ uint8 page_flag;
+ uint8 bytes_in_seg;
+ uint8 first_decode;
+ int next_seg;
+ int last_seg; // flag that we're on the last segment
+ int last_seg_which; // what was the segment number of the last seg?
+ uint32 acc;
+ int valid_bits;
+ int packet_bytes;
+ int end_seg_with_known_loc;
+ uint32 known_loc_for_packet;
+ int discard_samples_deferred;
+ uint32 samples_output;
+
+ // push mode scanning
+ int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+ CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT];
+#endif
+
+ // sample-access
+ int channel_buffer_start;
+ int channel_buffer_end;
+};
+
+#if defined(STB_VORBIS_NO_PUSHDATA_API)
+ #define IS_PUSH_MODE(f) FALSE
+#elif defined(STB_VORBIS_NO_PULLDATA_API)
+ #define IS_PUSH_MODE(f) TRUE
+#else
+ #define IS_PUSH_MODE(f) ((f)->push_mode)
+#endif
+
+typedef struct stb_vorbis vorb;
+
+static int error(vorb *f, enum STBVorbisError e)
+{
+ f->error = e;
+ if (!f->eof && e != VORBIS_need_more_data) {
+ f->error=e; // breakpoint for debugging
+ }
+ return 0;
+}
+
+
+// these functions are used for allocating temporary memory
+// while decoding. if you can afford the stack space, use
+// alloca(); otherwise, provide a temp buffer and it will
+// allocate out of those.
+
+#define array_size_required(count,size) (count*(sizeof(void *)+(size)))
+
+#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size))
+#define temp_free(f,p) 0
+#define temp_alloc_save(f) ((f)->temp_offset)
+#define temp_alloc_restore(f,p) ((f)->temp_offset = (p))
+
+#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size)
+
+// given a sufficiently large block of memory, make an array of pointers to subblocks of it
+static void *make_block_array(void *mem, int count, int size)
+{
+ int i;
+ void ** p = (void **) mem;
+ char *q = (char *) (p + count);
+ for (i=0; i < count; ++i) {
+ p[i] = q;
+ q += size;
+ }
+ return p;
+}
+
+static void *setup_malloc(vorb *f, int sz)
+{
+ sz = (sz+3) & ~3;
+ f->setup_memory_required += sz;
+ if (f->alloc.alloc_buffer) {
+ void *p = (char *) f->alloc.alloc_buffer + f->setup_offset;
+ if (f->setup_offset + sz > f->temp_offset) return NULL;
+ f->setup_offset += sz;
+ return p;
+ }
+ return sz ? malloc(sz) : NULL;
+}
+
+static void setup_free(vorb *f, void *p)
+{
+ if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack
+ free(p);
+}
+
+static void *setup_temp_malloc(vorb *f, int sz)
+{
+ sz = (sz+3) & ~3;
+ if (f->alloc.alloc_buffer) {
+ if (f->temp_offset - sz < f->setup_offset) return NULL;
+ f->temp_offset -= sz;
+ return (char *) f->alloc.alloc_buffer + f->temp_offset;
+ }
+ return malloc(sz);
+}
+
+static void setup_temp_free(vorb *f, void *p, int sz)
+{
+ if (f->alloc.alloc_buffer) {
+ f->temp_offset += (sz+3)&~3;
+ return;
+ }
+ free(p);
+}
+
+#define CRC32_POLY 0x04c11db7 // from spec
+
+static uint32 crc_table[256];
+static void crc32_init(void)
+{
+ int i,j;
+ uint32 s;
+ for(i=0; i < 256; i++) {
+ for (s=(uint32) i << 24, j=0; j < 8; ++j)
+ s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0);
+ crc_table[i] = s;
+ }
+}
+
+static __forceinline uint32 crc32_update(uint32 crc, uint8 byte)
+{
+ return (crc << 8) ^ crc_table[byte ^ (crc >> 24)];
+}
+
+
+// used in setup, and for huffman that doesn't go fast path
+static unsigned int bit_reverse(unsigned int n)
+{
+ n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1);
+ n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2);
+ n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4);
+ n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8);
+ return (n >> 16) | (n << 16);
+}
+
+static float square(float x)
+{
+ return x*x;
+}
+
+// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3
+// as required by the specification. fast(?) implementation from stb.h
+// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup
+static int ilog(int32 n)
+{
+ static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 };
+
+ if (n < 0) return 0; // signed n returns 0
+
+ // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29)
+ if (n < (1 << 14))
+ if (n < (1 << 4)) return 0 + log2_4[n ];
+ else if (n < (1 << 9)) return 5 + log2_4[n >> 5];
+ else return 10 + log2_4[n >> 10];
+ else if (n < (1 << 24))
+ if (n < (1 << 19)) return 15 + log2_4[n >> 15];
+ else return 20 + log2_4[n >> 20];
+ else if (n < (1 << 29)) return 25 + log2_4[n >> 25];
+ else return 30 + log2_4[n >> 30];
+}
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846264f // from CRC
+#endif
+
+// code length assigned to a value with no huffman encoding
+#define NO_CODE 255
+
+/////////////////////// LEAF SETUP FUNCTIONS //////////////////////////
+//
+// these functions are only called at setup, and only a few times
+// per file
+
+static float float32_unpack(uint32 x)
+{
+ // from the specification
+ uint32 mantissa = x & 0x1fffff;
+ uint32 sign = x & 0x80000000;
+ uint32 exp = (x & 0x7fe00000) >> 21;
+ double res = sign ? -(double)mantissa : (double)mantissa;
+ return (float) ldexp((float)res, exp-788);
+}
+
+
+// zlib & jpeg huffman tables assume that the output symbols
+// can either be arbitrarily arranged, or have monotonically
+// increasing frequencies--they rely on the lengths being sorted;
+// this makes for a very simple generation algorithm.
+// vorbis allows a huffman table with non-sorted lengths. This
+// requires a more sophisticated construction, since symbols in
+// order do not map to huffman codes "in order".
+static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values)
+{
+ if (!c->sparse) {
+ c->codewords [symbol] = huff_code;
+ } else {
+ c->codewords [count] = huff_code;
+ c->codeword_lengths[count] = len;
+ values [count] = symbol;
+ }
+}
+
+static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values)
+{
+ int i,k,m=0;
+ uint32 available[32];
+
+ memset(available, 0, sizeof(available));
+ // find the first entry
+ for (k=0; k < n; ++k) if (len[k] < NO_CODE) break;
+ if (k == n) { assert(c->sorted_entries == 0); return TRUE; }
+ // add to the list
+ add_entry(c, 0, k, m++, len[k], values);
+ // add all available leaves
+ for (i=1; i <= len[k]; ++i)
+ available[i] = 1U << (32-i);
+ // note that the above code treats the first case specially,
+ // but it's really the same as the following code, so they
+ // could probably be combined (except the initial code is 0,
+ // and I use 0 in available[] to mean 'empty')
+ for (i=k+1; i < n; ++i) {
+ uint32 res;
+ int z = len[i], y;
+ if (z == NO_CODE) continue;
+ // find lowest available leaf (should always be earliest,
+ // which is what the specification calls for)
+ // note that this property, and the fact we can never have
+ // more than one free leaf at a given level, isn't totally
+ // trivial to prove, but it seems true and the assert never
+ // fires, so!
+ while (z > 0 && !available[z]) --z;
+ if (z == 0) { return FALSE; }
+ res = available[z];
+ assert(z >= 0 && z < 32);
+ available[z] = 0;
+ add_entry(c, bit_reverse(res), i, m++, len[i], values);
+ // propogate availability up the tree
+ if (z != len[i]) {
+ assert(len[i] >= 0 && len[i] < 32);
+ for (y=len[i]; y > z; --y) {
+ assert(available[y] == 0);
+ available[y] = res + (1 << (32-y));
+ }
+ }
+ }
+ return TRUE;
+}
+
+// accelerated huffman table allows fast O(1) match of all symbols
+// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH
+static void compute_accelerated_huffman(Codebook *c)
+{
+ int i, len;
+ for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i)
+ c->fast_huffman[i] = -1;
+
+ len = c->sparse ? c->sorted_entries : c->entries;
+ #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT
+ if (len > 32767) len = 32767; // largest possible value we can encode!
+ #endif
+ for (i=0; i < len; ++i) {
+ if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) {
+ uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i];
+ // set table entries for all bit combinations in the higher bits
+ while (z < FAST_HUFFMAN_TABLE_SIZE) {
+ c->fast_huffman[z] = i;
+ z += 1 << c->codeword_lengths[i];
+ }
+ }
+ }
+}
+
+#ifdef _MSC_VER
+#define STBV_CDECL __cdecl
+#else
+#define STBV_CDECL
+#endif
+
+static int STBV_CDECL uint32_compare(const void *p, const void *q)
+{
+ uint32 x = * (uint32 *) p;
+ uint32 y = * (uint32 *) q;
+ return x < y ? -1 : x > y;
+}
+
+static int include_in_sort(Codebook *c, uint8 len)
+{
+ if (c->sparse) { assert(len != NO_CODE); return TRUE; }
+ if (len == NO_CODE) return FALSE;
+ if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE;
+ return FALSE;
+}
+
+// if the fast table above doesn't work, we want to binary
+// search them... need to reverse the bits
+static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values)
+{
+ int i, len;
+ // build a list of all the entries
+ // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN.
+ // this is kind of a frivolous optimization--I don't see any performance improvement,
+ // but it's like 4 extra lines of code, so.
+ if (!c->sparse) {
+ int k = 0;
+ for (i=0; i < c->entries; ++i)
+ if (include_in_sort(c, lengths[i]))
+ c->sorted_codewords[k++] = bit_reverse(c->codewords[i]);
+ assert(k == c->sorted_entries);
+ } else {
+ for (i=0; i < c->sorted_entries; ++i)
+ c->sorted_codewords[i] = bit_reverse(c->codewords[i]);
+ }
+
+ qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare);
+ c->sorted_codewords[c->sorted_entries] = 0xffffffff;
+
+ len = c->sparse ? c->sorted_entries : c->entries;
+ // now we need to indicate how they correspond; we could either
+ // #1: sort a different data structure that says who they correspond to
+ // #2: for each sorted entry, search the original list to find who corresponds
+ // #3: for each original entry, find the sorted entry
+ // #1 requires extra storage, #2 is slow, #3 can use binary search!
+ for (i=0; i < len; ++i) {
+ int huff_len = c->sparse ? lengths[values[i]] : lengths[i];
+ if (include_in_sort(c,huff_len)) {
+ uint32 code = bit_reverse(c->codewords[i]);
+ int x=0, n=c->sorted_entries;
+ while (n > 1) {
+ // invariant: sc[x] <= code < sc[x+n]
+ int m = x + (n >> 1);
+ if (c->sorted_codewords[m] <= code) {
+ x = m;
+ n -= (n>>1);
+ } else {
+ n >>= 1;
+ }
+ }
+ assert(c->sorted_codewords[x] == code);
+ if (c->sparse) {
+ c->sorted_values[x] = values[i];
+ c->codeword_lengths[x] = huff_len;
+ } else {
+ c->sorted_values[x] = i;
+ }
+ }
+ }
+}
+
+// only run while parsing the header (3 times)
+static int vorbis_validate(uint8 *data)
+{
+ static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' };
+ return memcmp(data, vorbis, 6) == 0;
+}
+
+// called from setup only, once per code book
+// (formula implied by specification)
+static int lookup1_values(int entries, int dim)
+{
+ int r = (int) floor(exp((float) log((float) entries) / dim));
+ if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning;
+ ++r; // floor() to avoid _ftol() when non-CRT
+ assert(pow((float) r+1, dim) > entries);
+ assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above
+ return r;
+}
+
+// called twice per file
+static void compute_twiddle_factors(int n, float *A, float *B, float *C)
+{
+ int n4 = n >> 2, n8 = n >> 3;
+ int k,k2;
+
+ for (k=k2=0; k < n4; ++k,k2+=2) {
+ A[k2 ] = (float) cos(4*k*M_PI/n);
+ A[k2+1] = (float) -sin(4*k*M_PI/n);
+ B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f;
+ B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f;
+ }
+ for (k=k2=0; k < n8; ++k,k2+=2) {
+ C[k2 ] = (float) cos(2*(k2+1)*M_PI/n);
+ C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n);
+ }
+}
+
+static void compute_window(int n, float *window)
+{
+ int n2 = n >> 1, i;
+ for (i=0; i < n2; ++i)
+ window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI)));
+}
+
+static void compute_bitreverse(int n, uint16 *rev)
+{
+ int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+ int i, n8 = n >> 3;
+ for (i=0; i < n8; ++i)
+ rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2;
+}
+
+static int init_blocksize(vorb *f, int b, int n)
+{
+ int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3;
+ f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+ f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+ f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4);
+ if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem);
+ compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]);
+ f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2);
+ if (!f->window[b]) return error(f, VORBIS_outofmem);
+ compute_window(n, f->window[b]);
+ f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8);
+ if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem);
+ compute_bitreverse(n, f->bit_reverse[b]);
+ return TRUE;
+}
+
+static void neighbors(uint16 *x, int n, int *plow, int *phigh)
+{
+ int low = -1;
+ int high = 65536;
+ int i;
+ for (i=0; i < n; ++i) {
+ if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; }
+ if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; }
+ }
+}
+
+// this has been repurposed so y is now the original index instead of y
+typedef struct
+{
+ uint16 x,id;
+} stbv__floor_ordering;
+
+static int STBV_CDECL point_compare(const void *p, const void *q)
+{
+ stbv__floor_ordering *a = (stbv__floor_ordering *) p;
+ stbv__floor_ordering *b = (stbv__floor_ordering *) q;
+ return a->x < b->x ? -1 : a->x > b->x;
+}
+
+//
+/////////////////////// END LEAF SETUP FUNCTIONS //////////////////////////
+
+
+#if defined(STB_VORBIS_NO_STDIO)
+ #define USE_MEMORY(z) TRUE
+#else
+ #define USE_MEMORY(z) ((z)->stream)
+#endif
+
+static uint8 get8(vorb *z)
+{
+ if (USE_MEMORY(z)) {
+ if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; }
+ return *z->stream++;
+ }
+
+ #ifndef STB_VORBIS_NO_STDIO
+ {
+ int c = fgetc(z->f);
+ if (c == EOF) { z->eof = TRUE; return 0; }
+ return c;
+ }
+ #endif
+}
+
+static uint32 get32(vorb *f)
+{
+ uint32 x;
+ x = get8(f);
+ x += get8(f) << 8;
+ x += get8(f) << 16;
+ x += (uint32) get8(f) << 24;
+ return x;
+}
+
+static int getn(vorb *z, uint8 *data, int n)
+{
+ if (USE_MEMORY(z)) {
+ if (z->stream+n > z->stream_end) { z->eof = 1; return 0; }
+ memcpy(data, z->stream, n);
+ z->stream += n;
+ return 1;
+ }
+
+ #ifndef STB_VORBIS_NO_STDIO
+ if (fread(data, n, 1, z->f) == 1)
+ return 1;
+ else {
+ z->eof = 1;
+ return 0;
+ }
+ #endif
+}
+
+static void skip(vorb *z, int n)
+{
+ if (USE_MEMORY(z)) {
+ z->stream += n;
+ if (z->stream >= z->stream_end) z->eof = 1;
+ return;
+ }
+ #ifndef STB_VORBIS_NO_STDIO
+ {
+ long x = ftell(z->f);
+ fseek(z->f, x+n, SEEK_SET);
+ }
+ #endif
+}
+
+static int set_file_offset(stb_vorbis *f, unsigned int loc)
+{
+ #ifndef STB_VORBIS_NO_PUSHDATA_API
+ if (f->push_mode) return 0;
+ #endif
+ f->eof = 0;
+ if (USE_MEMORY(f)) {
+ if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) {
+ f->stream = f->stream_end;
+ f->eof = 1;
+ return 0;
+ } else {
+ f->stream = f->stream_start + loc;
+ return 1;
+ }
+ }
+ #ifndef STB_VORBIS_NO_STDIO
+ if (loc + f->f_start < loc || loc >= 0x80000000) {
+ loc = 0x7fffffff;
+ f->eof = 1;
+ } else {
+ loc += f->f_start;
+ }
+ if (!fseek(f->f, loc, SEEK_SET))
+ return 1;
+ f->eof = 1;
+ fseek(f->f, f->f_start, SEEK_END);
+ return 0;
+ #endif
+}
+
+
+static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 };
+
+static int capture_pattern(vorb *f)
+{
+ if (0x4f != get8(f)) return FALSE;
+ if (0x67 != get8(f)) return FALSE;
+ if (0x67 != get8(f)) return FALSE;
+ if (0x53 != get8(f)) return FALSE;
+ return TRUE;
+}
+
+#define PAGEFLAG_continued_packet 1
+#define PAGEFLAG_first_page 2
+#define PAGEFLAG_last_page 4
+
+static int start_page_no_capturepattern(vorb *f)
+{
+ uint32 loc0,loc1,n;
+ // stream structure version
+ if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version);
+ // header flag
+ f->page_flag = get8(f);
+ // absolute granule position
+ loc0 = get32(f);
+ loc1 = get32(f);
+ // @TODO: validate loc0,loc1 as valid positions?
+ // stream serial number -- vorbis doesn't interleave, so discard
+ get32(f);
+ //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number);
+ // page sequence number
+ n = get32(f);
+ f->last_page = n;
+ // CRC32
+ get32(f);
+ // page_segments
+ f->segment_count = get8(f);
+ if (!getn(f, f->segments, f->segment_count))
+ return error(f, VORBIS_unexpected_eof);
+ // assume we _don't_ know any the sample position of any segments
+ f->end_seg_with_known_loc = -2;
+ if (loc0 != ~0U || loc1 != ~0U) {
+ int i;
+ // determine which packet is the last one that will complete
+ for (i=f->segment_count-1; i >= 0; --i)
+ if (f->segments[i] < 255)
+ break;
+ // 'i' is now the index of the _last_ segment of a packet that ends
+ if (i >= 0) {
+ f->end_seg_with_known_loc = i;
+ f->known_loc_for_packet = loc0;
+ }
+ }
+ if (f->first_decode) {
+ int i,len;
+ ProbedPage p;
+ len = 0;
+ for (i=0; i < f->segment_count; ++i)
+ len += f->segments[i];
+ len += 27 + f->segment_count;
+ p.page_start = f->first_audio_page_offset;
+ p.page_end = p.page_start + len;
+ p.last_decoded_sample = loc0;
+ f->p_first = p;
+ }
+ f->next_seg = 0;
+ return TRUE;
+}
+
+static int start_page(vorb *f)
+{
+ if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern);
+ return start_page_no_capturepattern(f);
+}
+
+static int start_packet(vorb *f)
+{
+ while (f->next_seg == -1) {
+ if (!start_page(f)) return FALSE;
+ if (f->page_flag & PAGEFLAG_continued_packet)
+ return error(f, VORBIS_continued_packet_flag_invalid);
+ }
+ f->last_seg = FALSE;
+ f->valid_bits = 0;
+ f->packet_bytes = 0;
+ f->bytes_in_seg = 0;
+ // f->next_seg is now valid
+ return TRUE;
+}
+
+static int maybe_start_packet(vorb *f)
+{
+ if (f->next_seg == -1) {
+ int x = get8(f);
+ if (f->eof) return FALSE; // EOF at page boundary is not an error!
+ if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern);
+ if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+ if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+ if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern);
+ if (!start_page_no_capturepattern(f)) return FALSE;
+ if (f->page_flag & PAGEFLAG_continued_packet) {
+ // set up enough state that we can read this packet if we want,
+ // e.g. during recovery
+ f->last_seg = FALSE;
+ f->bytes_in_seg = 0;
+ return error(f, VORBIS_continued_packet_flag_invalid);
+ }
+ }
+ return start_packet(f);
+}
+
+static int next_segment(vorb *f)
+{
+ int len;
+ if (f->last_seg) return 0;
+ if (f->next_seg == -1) {
+ f->last_seg_which = f->segment_count-1; // in case start_page fails
+ if (!start_page(f)) { f->last_seg = 1; return 0; }
+ if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid);
+ }
+ len = f->segments[f->next_seg++];
+ if (len < 255) {
+ f->last_seg = TRUE;
+ f->last_seg_which = f->next_seg-1;
+ }
+ if (f->next_seg >= f->segment_count)
+ f->next_seg = -1;
+ assert(f->bytes_in_seg == 0);
+ f->bytes_in_seg = len;
+ return len;
+}
+
+#define EOP (-1)
+#define INVALID_BITS (-1)
+
+static int get8_packet_raw(vorb *f)
+{
+ if (!f->bytes_in_seg) { // CLANG!
+ if (f->last_seg) return EOP;
+ else if (!next_segment(f)) return EOP;
+ }
+ assert(f->bytes_in_seg > 0);
+ --f->bytes_in_seg;
+ ++f->packet_bytes;
+ return get8(f);
+}
+
+static int get8_packet(vorb *f)
+{
+ int x = get8_packet_raw(f);
+ f->valid_bits = 0;
+ return x;
+}
+
+static void flush_packet(vorb *f)
+{
+ while (get8_packet_raw(f) != EOP);
+}
+
+// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important
+// as the huffman decoder?
+static uint32 get_bits(vorb *f, int n)
+{
+ uint32 z;
+
+ if (f->valid_bits < 0) return 0;
+ if (f->valid_bits < n) {
+ if (n > 24) {
+ // the accumulator technique below would not work correctly in this case
+ z = get_bits(f, 24);
+ z += get_bits(f, n-24) << 24;
+ return z;
+ }
+ if (f->valid_bits == 0) f->acc = 0;
+ while (f->valid_bits < n) {
+ int z = get8_packet_raw(f);
+ if (z == EOP) {
+ f->valid_bits = INVALID_BITS;
+ return 0;
+ }
+ f->acc += z << f->valid_bits;
+ f->valid_bits += 8;
+ }
+ }
+ if (f->valid_bits < 0) return 0;
+ z = f->acc & ((1 << n)-1);
+ f->acc >>= n;
+ f->valid_bits -= n;
+ return z;
+}
+
+// @OPTIMIZE: primary accumulator for huffman
+// expand the buffer to as many bits as possible without reading off end of packet
+// it might be nice to allow f->valid_bits and f->acc to be stored in registers,
+// e.g. cache them locally and decode locally
+static __forceinline void prep_huffman(vorb *f)
+{
+ if (f->valid_bits <= 24) {
+ if (f->valid_bits == 0) f->acc = 0;
+ do {
+ int z;
+ if (f->last_seg && !f->bytes_in_seg) return;
+ z = get8_packet_raw(f);
+ if (z == EOP) return;
+ f->acc += (unsigned) z << f->valid_bits;
+ f->valid_bits += 8;
+ } while (f->valid_bits <= 24);
+ }
+}
+
+enum
+{
+ VORBIS_packet_id = 1,
+ VORBIS_packet_comment = 3,
+ VORBIS_packet_setup = 5
+};
+
+static int codebook_decode_scalar_raw(vorb *f, Codebook *c)
+{
+ int i;
+ prep_huffman(f);
+
+ if (c->codewords == NULL && c->sorted_codewords == NULL)
+ return -1;
+
+ // cases to use binary search: sorted_codewords && !c->codewords
+ // sorted_codewords && c->entries > 8
+ if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) {
+ // binary search
+ uint32 code = bit_reverse(f->acc);
+ int x=0, n=c->sorted_entries, len;
+
+ while (n > 1) {
+ // invariant: sc[x] <= code < sc[x+n]
+ int m = x + (n >> 1);
+ if (c->sorted_codewords[m] <= code) {
+ x = m;
+ n -= (n>>1);
+ } else {
+ n >>= 1;
+ }
+ }
+ // x is now the sorted index
+ if (!c->sparse) x = c->sorted_values[x];
+ // x is now sorted index if sparse, or symbol otherwise
+ len = c->codeword_lengths[x];
+ if (f->valid_bits >= len) {
+ f->acc >>= len;
+ f->valid_bits -= len;
+ return x;
+ }
+
+ f->valid_bits = 0;
+ return -1;
+ }
+
+ // if small, linear search
+ assert(!c->sparse);
+ for (i=0; i < c->entries; ++i) {
+ if (c->codeword_lengths[i] == NO_CODE) continue;
+ if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) {
+ if (f->valid_bits >= c->codeword_lengths[i]) {
+ f->acc >>= c->codeword_lengths[i];
+ f->valid_bits -= c->codeword_lengths[i];
+ return i;
+ }
+ f->valid_bits = 0;
+ return -1;
+ }
+ }
+
+ error(f, VORBIS_invalid_stream);
+ f->valid_bits = 0;
+ return -1;
+}
+
+#ifndef STB_VORBIS_NO_INLINE_DECODE
+
+#define DECODE_RAW(var, f,c) \
+ if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \
+ prep_huffman(f); \
+ var = f->acc & FAST_HUFFMAN_TABLE_MASK; \
+ var = c->fast_huffman[var]; \
+ if (var >= 0) { \
+ int n = c->codeword_lengths[var]; \
+ f->acc >>= n; \
+ f->valid_bits -= n; \
+ if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \
+ } else { \
+ var = codebook_decode_scalar_raw(f,c); \
+ }
+
+#else
+
+static int codebook_decode_scalar(vorb *f, Codebook *c)
+{
+ int i;
+ if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH)
+ prep_huffman(f);
+ // fast huffman table lookup
+ i = f->acc & FAST_HUFFMAN_TABLE_MASK;
+ i = c->fast_huffman[i];
+ if (i >= 0) {
+ f->acc >>= c->codeword_lengths[i];
+ f->valid_bits -= c->codeword_lengths[i];
+ if (f->valid_bits < 0) { f->valid_bits = 0; return -1; }
+ return i;
+ }
+ return codebook_decode_scalar_raw(f,c);
+}
+
+#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c);
+
+#endif
+
+#define DECODE(var,f,c) \
+ DECODE_RAW(var,f,c) \
+ if (c->sparse) var = c->sorted_values[var];
+
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c)
+#else
+ #define DECODE_VQ(var,f,c) DECODE(var,f,c)
+#endif
+
+
+
+
+
+
+// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case
+// where we avoid one addition
+#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off])
+#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off])
+#define CODEBOOK_ELEMENT_BASE(c) (0)
+
+static int codebook_decode_start(vorb *f, Codebook *c)
+{
+ int z = -1;
+
+ // type 0 is only legal in a scalar context
+ if (c->lookup_type == 0)
+ error(f, VORBIS_invalid_stream);
+ else {
+ DECODE_VQ(z,f,c);
+ if (c->sparse) assert(z < c->sorted_entries);
+ if (z < 0) { // check for EOP
+ if (!f->bytes_in_seg)
+ if (f->last_seg)
+ return z;
+ error(f, VORBIS_invalid_stream);
+ }
+ }
+ return z;
+}
+
+static int codebook_decode(vorb *f, Codebook *c, float *output, int len)
+{
+ int i,z = codebook_decode_start(f,c);
+ if (z < 0) return FALSE;
+ if (len > c->dimensions) len = c->dimensions;
+
+#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (c->lookup_type == 1) {
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ int div = 1;
+ for (i=0; i < len; ++i) {
+ int off = (z / div) % c->lookup_values;
+ float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+ output[i] += val;
+ if (c->sequence_p) last = val + c->minimum_value;
+ div *= c->lookup_values;
+ }
+ return TRUE;
+ }
+#endif
+
+ z *= c->dimensions;
+ if (c->sequence_p) {
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ for (i=0; i < len; ++i) {
+ float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ output[i] += val;
+ last = val + c->minimum_value;
+ }
+ } else {
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ for (i=0; i < len; ++i) {
+ output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ }
+ }
+
+ return TRUE;
+}
+
+static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step)
+{
+ int i,z = codebook_decode_start(f,c);
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ if (z < 0) return FALSE;
+ if (len > c->dimensions) len = c->dimensions;
+
+#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (c->lookup_type == 1) {
+ int div = 1;
+ for (i=0; i < len; ++i) {
+ int off = (z / div) % c->lookup_values;
+ float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+ output[i*step] += val;
+ if (c->sequence_p) last = val;
+ div *= c->lookup_values;
+ }
+ return TRUE;
+ }
+#endif
+
+ z *= c->dimensions;
+ for (i=0; i < len; ++i) {
+ float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ output[i*step] += val;
+ if (c->sequence_p) last = val;
+ }
+
+ return TRUE;
+}
+
+static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode)
+{
+ int c_inter = *c_inter_p;
+ int p_inter = *p_inter_p;
+ int i,z, effective = c->dimensions;
+
+ // type 0 is only legal in a scalar context
+ if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream);
+
+ while (total_decode > 0) {
+ float last = CODEBOOK_ELEMENT_BASE(c);
+ DECODE_VQ(z,f,c);
+ #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ assert(!c->sparse || z < c->sorted_entries);
+ #endif
+ if (z < 0) {
+ if (!f->bytes_in_seg)
+ if (f->last_seg) return FALSE;
+ return error(f, VORBIS_invalid_stream);
+ }
+
+ // if this will take us off the end of the buffers, stop short!
+ // we check by computing the length of the virtual interleaved
+ // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter),
+ // and the length we'll be using (effective)
+ if (c_inter + p_inter*ch + effective > len * ch) {
+ effective = len*ch - (p_inter*ch - c_inter);
+ }
+
+ #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (c->lookup_type == 1) {
+ int div = 1;
+ for (i=0; i < effective; ++i) {
+ int off = (z / div) % c->lookup_values;
+ float val = CODEBOOK_ELEMENT_FAST(c,off) + last;
+ if (outputs[c_inter])
+ outputs[c_inter][p_inter] += val;
+ if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+ if (c->sequence_p) last = val;
+ div *= c->lookup_values;
+ }
+ } else
+ #endif
+ {
+ z *= c->dimensions;
+ if (c->sequence_p) {
+ for (i=0; i < effective; ++i) {
+ float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ if (outputs[c_inter])
+ outputs[c_inter][p_inter] += val;
+ if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+ last = val;
+ }
+ } else {
+ for (i=0; i < effective; ++i) {
+ float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
+ if (outputs[c_inter])
+ outputs[c_inter][p_inter] += val;
+ if (++c_inter == ch) { c_inter = 0; ++p_inter; }
+ }
+ }
+ }
+
+ total_decode -= effective;
+ }
+ *c_inter_p = c_inter;
+ *p_inter_p = p_inter;
+ return TRUE;
+}
+
+static int predict_point(int x, int x0, int x1, int y0, int y1)
+{
+ int dy = y1 - y0;
+ int adx = x1 - x0;
+ // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86?
+ int err = abs(dy) * (x - x0);
+ int off = err / adx;
+ return dy < 0 ? y0 - off : y0 + off;
+}
+
+// the following table is block-copied from the specification
+static float inverse_db_table[256] =
+{
+ 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f,
+ 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f,
+ 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f,
+ 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f,
+ 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f,
+ 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f,
+ 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f,
+ 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f,
+ 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f,
+ 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f,
+ 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f,
+ 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f,
+ 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f,
+ 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f,
+ 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f,
+ 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f,
+ 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f,
+ 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f,
+ 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f,
+ 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f,
+ 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f,
+ 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f,
+ 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f,
+ 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f,
+ 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f,
+ 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f,
+ 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f,
+ 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f,
+ 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f,
+ 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f,
+ 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f,
+ 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f,
+ 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f,
+ 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f,
+ 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f,
+ 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f,
+ 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f,
+ 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f,
+ 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f,
+ 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f,
+ 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f,
+ 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f,
+ 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f,
+ 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f,
+ 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f,
+ 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f,
+ 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f,
+ 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f,
+ 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f,
+ 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f,
+ 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f,
+ 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f,
+ 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f,
+ 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f,
+ 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f,
+ 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f,
+ 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f,
+ 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f,
+ 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f,
+ 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f,
+ 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f,
+ 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f,
+ 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f,
+ 0.82788260f, 0.88168307f, 0.9389798f, 1.0f
+};
+
+
+// @OPTIMIZE: if you want to replace this bresenham line-drawing routine,
+// note that you must produce bit-identical output to decode correctly;
+// this specific sequence of operations is specified in the spec (it's
+// drawing integer-quantized frequency-space lines that the encoder
+// expects to be exactly the same)
+// ... also, isn't the whole point of Bresenham's algorithm to NOT
+// have to divide in the setup? sigh.
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+#define LINE_OP(a,b) a *= b
+#else
+#define LINE_OP(a,b) a = b
+#endif
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+#define DIVTAB_NUMER 32
+#define DIVTAB_DENOM 64
+int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB
+#endif
+
+static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n)
+{
+ int dy = y1 - y0;
+ int adx = x1 - x0;
+ int ady = abs(dy);
+ int base;
+ int x=x0,y=y0;
+ int err = 0;
+ int sy;
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+ if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) {
+ if (dy < 0) {
+ base = -integer_divide_table[ady][adx];
+ sy = base-1;
+ } else {
+ base = integer_divide_table[ady][adx];
+ sy = base+1;
+ }
+ } else {
+ base = dy / adx;
+ if (dy < 0)
+ sy = base - 1;
+ else
+ sy = base+1;
+ }
+#else
+ base = dy / adx;
+ if (dy < 0)
+ sy = base - 1;
+ else
+ sy = base+1;
+#endif
+ ady -= abs(base) * adx;
+ if (x1 > n) x1 = n;
+ if (x < x1) {
+ LINE_OP(output[x], inverse_db_table[y]);
+ for (++x; x < x1; ++x) {
+ err += ady;
+ if (err >= adx) {
+ err -= adx;
+ y += sy;
+ } else
+ y += base;
+ LINE_OP(output[x], inverse_db_table[y]);
+ }
+ }
+}
+
+static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype)
+{
+ int k;
+ if (rtype == 0) {
+ int step = n / book->dimensions;
+ for (k=0; k < step; ++k)
+ if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step))
+ return FALSE;
+ } else {
+ for (k=0; k < n; ) {
+ if (!codebook_decode(f, book, target+offset, n-k))
+ return FALSE;
+ k += book->dimensions;
+ offset += book->dimensions;
+ }
+ }
+ return TRUE;
+}
+
+// n is 1/2 of the blocksize --
+// specification: "Correct per-vector decode length is [n]/2"
+static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode)
+{
+ int i,j,pass;
+ Residue *r = f->residue_config + rn;
+ int rtype = f->residue_types[rn];
+ int c = r->classbook;
+ int classwords = f->codebooks[c].dimensions;
+ unsigned int actual_size = rtype == 2 ? n*2 : n;
+ unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size);
+ unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size);
+ int n_read = limit_r_end - limit_r_begin;
+ int part_read = n_read / r->part_size;
+ int temp_alloc_point = temp_alloc_save(f);
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata));
+ #else
+ int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications));
+ #endif
+
+ CHECK(f);
+
+ for (i=0; i < ch; ++i)
+ if (!do_not_decode[i])
+ memset(residue_buffers[i], 0, sizeof(float) * n);
+
+ if (rtype == 2 && ch != 1) {
+ for (j=0; j < ch; ++j)
+ if (!do_not_decode[j])
+ break;
+ if (j == ch)
+ goto done;
+
+ for (pass=0; pass < 8; ++pass) {
+ int pcount = 0, class_set = 0;
+ if (ch == 2) {
+ while (pcount < part_read) {
+ int z = r->begin + pcount*r->part_size;
+ int c_inter = (z & 1), p_inter = z>>1;
+ if (pass == 0) {
+ Codebook *c = f->codebooks+r->classbook;
+ int q;
+ DECODE(q,f,c);
+ if (q == EOP) goto done;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ part_classdata[0][class_set] = r->classdata[q];
+ #else
+ for (i=classwords-1; i >= 0; --i) {
+ classifications[0][i+pcount] = q % r->classifications;
+ q /= r->classifications;
+ }
+ #endif
+ }
+ for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+ int z = r->begin + pcount*r->part_size;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ int c = part_classdata[0][class_set][i];
+ #else
+ int c = classifications[0][pcount];
+ #endif
+ int b = r->residue_books[c][pass];
+ if (b >= 0) {
+ Codebook *book = f->codebooks + b;
+ #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+ goto done;
+ #else
+ // saves 1%
+ if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+ goto done;
+ #endif
+ } else {
+ z += r->part_size;
+ c_inter = z & 1;
+ p_inter = z >> 1;
+ }
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ ++class_set;
+ #endif
+ }
+ } else if (ch == 1) {
+ while (pcount < part_read) {
+ int z = r->begin + pcount*r->part_size;
+ int c_inter = 0, p_inter = z;
+ if (pass == 0) {
+ Codebook *c = f->codebooks+r->classbook;
+ int q;
+ DECODE(q,f,c);
+ if (q == EOP) goto done;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ part_classdata[0][class_set] = r->classdata[q];
+ #else
+ for (i=classwords-1; i >= 0; --i) {
+ classifications[0][i+pcount] = q % r->classifications;
+ q /= r->classifications;
+ }
+ #endif
+ }
+ for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+ int z = r->begin + pcount*r->part_size;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ int c = part_classdata[0][class_set][i];
+ #else
+ int c = classifications[0][pcount];
+ #endif
+ int b = r->residue_books[c][pass];
+ if (b >= 0) {
+ Codebook *book = f->codebooks + b;
+ if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+ goto done;
+ } else {
+ z += r->part_size;
+ c_inter = 0;
+ p_inter = z;
+ }
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ ++class_set;
+ #endif
+ }
+ } else {
+ while (pcount < part_read) {
+ int z = r->begin + pcount*r->part_size;
+ int c_inter = z % ch, p_inter = z/ch;
+ if (pass == 0) {
+ Codebook *c = f->codebooks+r->classbook;
+ int q;
+ DECODE(q,f,c);
+ if (q == EOP) goto done;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ part_classdata[0][class_set] = r->classdata[q];
+ #else
+ for (i=classwords-1; i >= 0; --i) {
+ classifications[0][i+pcount] = q % r->classifications;
+ q /= r->classifications;
+ }
+ #endif
+ }
+ for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+ int z = r->begin + pcount*r->part_size;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ int c = part_classdata[0][class_set][i];
+ #else
+ int c = classifications[0][pcount];
+ #endif
+ int b = r->residue_books[c][pass];
+ if (b >= 0) {
+ Codebook *book = f->codebooks + b;
+ if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size))
+ goto done;
+ } else {
+ z += r->part_size;
+ c_inter = z % ch;
+ p_inter = z / ch;
+ }
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ ++class_set;
+ #endif
+ }
+ }
+ }
+ goto done;
+ }
+ CHECK(f);
+
+ for (pass=0; pass < 8; ++pass) {
+ int pcount = 0, class_set=0;
+ while (pcount < part_read) {
+ if (pass == 0) {
+ for (j=0; j < ch; ++j) {
+ if (!do_not_decode[j]) {
+ Codebook *c = f->codebooks+r->classbook;
+ int temp;
+ DECODE(temp,f,c);
+ if (temp == EOP) goto done;
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ part_classdata[j][class_set] = r->classdata[temp];
+ #else
+ for (i=classwords-1; i >= 0; --i) {
+ classifications[j][i+pcount] = temp % r->classifications;
+ temp /= r->classifications;
+ }
+ #endif
+ }
+ }
+ }
+ for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) {
+ for (j=0; j < ch; ++j) {
+ if (!do_not_decode[j]) {
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ int c = part_classdata[j][class_set][i];
+ #else
+ int c = classifications[j][pcount];
+ #endif
+ int b = r->residue_books[c][pass];
+ if (b >= 0) {
+ float *target = residue_buffers[j];
+ int offset = r->begin + pcount * r->part_size;
+ int n = r->part_size;
+ Codebook *book = f->codebooks + b;
+ if (!residue_decode(f, book, target, offset, n, rtype))
+ goto done;
+ }
+ }
+ }
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ ++class_set;
+ #endif
+ }
+ }
+ done:
+ CHECK(f);
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ temp_free(f,part_classdata);
+ #else
+ temp_free(f,classifications);
+ #endif
+ temp_alloc_restore(f,temp_alloc_point);
+}
+
+
+#if 0
+// slow way for debugging
+void inverse_mdct_slow(float *buffer, int n)
+{
+ int i,j;
+ int n2 = n >> 1;
+ float *x = (float *) malloc(sizeof(*x) * n2);
+ memcpy(x, buffer, sizeof(*x) * n2);
+ for (i=0; i < n; ++i) {
+ float acc = 0;
+ for (j=0; j < n2; ++j)
+ // formula from paper:
+ //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1));
+ // formula from wikipedia
+ //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5));
+ // these are equivalent, except the formula from the paper inverts the multiplier!
+ // however, what actually works is NO MULTIPLIER!?!
+ //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5));
+ acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1));
+ buffer[i] = acc;
+ }
+ free(x);
+}
+#elif 0
+// same as above, but just barely able to run in real time on modern machines
+void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
+{
+ float mcos[16384];
+ int i,j;
+ int n2 = n >> 1, nmask = (n << 2) -1;
+ float *x = (float *) malloc(sizeof(*x) * n2);
+ memcpy(x, buffer, sizeof(*x) * n2);
+ for (i=0; i < 4*n; ++i)
+ mcos[i] = (float) cos(M_PI / 2 * i / n);
+
+ for (i=0; i < n; ++i) {
+ float acc = 0;
+ for (j=0; j < n2; ++j)
+ acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask];
+ buffer[i] = acc;
+ }
+ free(x);
+}
+#elif 0
+// transform to use a slow dct-iv; this is STILL basically trivial,
+// but only requires half as many ops
+void dct_iv_slow(float *buffer, int n)
+{
+ float mcos[16384];
+ float x[2048];
+ int i,j;
+ int n2 = n >> 1, nmask = (n << 3) - 1;
+ memcpy(x, buffer, sizeof(*x) * n);
+ for (i=0; i < 8*n; ++i)
+ mcos[i] = (float) cos(M_PI / 4 * i / n);
+ for (i=0; i < n; ++i) {
+ float acc = 0;
+ for (j=0; j < n; ++j)
+ acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask];
+ buffer[i] = acc;
+ }
+}
+
+void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype)
+{
+ int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4;
+ float temp[4096];
+
+ memcpy(temp, buffer, n2 * sizeof(float));
+ dct_iv_slow(temp, n2); // returns -c'-d, a-b'
+
+ for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b'
+ for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d'
+ for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d
+}
+#endif
+
+#ifndef LIBVORBIS_MDCT
+#define LIBVORBIS_MDCT 0
+#endif
+
+#if LIBVORBIS_MDCT
+// directly call the vorbis MDCT using an interface documented
+// by Jeff Roberts... useful for performance comparison
+typedef struct
+{
+ int n;
+ int log2n;
+
+ float *trig;
+ int *bitrev;
+
+ float scale;
+} mdct_lookup;
+
+extern void mdct_init(mdct_lookup *lookup, int n);
+extern void mdct_clear(mdct_lookup *l);
+extern void mdct_backward(mdct_lookup *init, float *in, float *out);
+
+mdct_lookup M1,M2;
+
+void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
+{
+ mdct_lookup *M;
+ if (M1.n == n) M = &M1;
+ else if (M2.n == n) M = &M2;
+ else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; }
+ else {
+ if (M2.n) __asm int 3;
+ mdct_init(&M2, n);
+ M = &M2;
+ }
+
+ mdct_backward(M, buffer, buffer);
+}
+#endif
+
+
+// the following were split out into separate functions while optimizing;
+// they could be pushed back up but eh. __forceinline showed no change;
+// they're probably already being inlined.
+static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A)
+{
+ float *ee0 = e + i_off;
+ float *ee2 = ee0 + k_off;
+ int i;
+
+ assert((n & 3) == 0);
+ for (i=(n>>2); i > 0; --i) {
+ float k00_20, k01_21;
+ k00_20 = ee0[ 0] - ee2[ 0];
+ k01_21 = ee0[-1] - ee2[-1];
+ ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0];
+ ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1];
+ ee2[ 0] = k00_20 * A[0] - k01_21 * A[1];
+ ee2[-1] = k01_21 * A[0] + k00_20 * A[1];
+ A += 8;
+
+ k00_20 = ee0[-2] - ee2[-2];
+ k01_21 = ee0[-3] - ee2[-3];
+ ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2];
+ ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3];
+ ee2[-2] = k00_20 * A[0] - k01_21 * A[1];
+ ee2[-3] = k01_21 * A[0] + k00_20 * A[1];
+ A += 8;
+
+ k00_20 = ee0[-4] - ee2[-4];
+ k01_21 = ee0[-5] - ee2[-5];
+ ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4];
+ ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5];
+ ee2[-4] = k00_20 * A[0] - k01_21 * A[1];
+ ee2[-5] = k01_21 * A[0] + k00_20 * A[1];
+ A += 8;
+
+ k00_20 = ee0[-6] - ee2[-6];
+ k01_21 = ee0[-7] - ee2[-7];
+ ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6];
+ ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7];
+ ee2[-6] = k00_20 * A[0] - k01_21 * A[1];
+ ee2[-7] = k01_21 * A[0] + k00_20 * A[1];
+ A += 8;
+ ee0 -= 8;
+ ee2 -= 8;
+ }
+}
+
+static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1)
+{
+ int i;
+ float k00_20, k01_21;
+
+ float *e0 = e + d0;
+ float *e2 = e0 + k_off;
+
+ for (i=lim >> 2; i > 0; --i) {
+ k00_20 = e0[-0] - e2[-0];
+ k01_21 = e0[-1] - e2[-1];
+ e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0];
+ e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1];
+ e2[-0] = (k00_20)*A[0] - (k01_21) * A[1];
+ e2[-1] = (k01_21)*A[0] + (k00_20) * A[1];
+
+ A += k1;
+
+ k00_20 = e0[-2] - e2[-2];
+ k01_21 = e0[-3] - e2[-3];
+ e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2];
+ e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3];
+ e2[-2] = (k00_20)*A[0] - (k01_21) * A[1];
+ e2[-3] = (k01_21)*A[0] + (k00_20) * A[1];
+
+ A += k1;
+
+ k00_20 = e0[-4] - e2[-4];
+ k01_21 = e0[-5] - e2[-5];
+ e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4];
+ e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5];
+ e2[-4] = (k00_20)*A[0] - (k01_21) * A[1];
+ e2[-5] = (k01_21)*A[0] + (k00_20) * A[1];
+
+ A += k1;
+
+ k00_20 = e0[-6] - e2[-6];
+ k01_21 = e0[-7] - e2[-7];
+ e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6];
+ e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7];
+ e2[-6] = (k00_20)*A[0] - (k01_21) * A[1];
+ e2[-7] = (k01_21)*A[0] + (k00_20) * A[1];
+
+ e0 -= 8;
+ e2 -= 8;
+
+ A += k1;
+ }
+}
+
+static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0)
+{
+ int i;
+ float A0 = A[0];
+ float A1 = A[0+1];
+ float A2 = A[0+a_off];
+ float A3 = A[0+a_off+1];
+ float A4 = A[0+a_off*2+0];
+ float A5 = A[0+a_off*2+1];
+ float A6 = A[0+a_off*3+0];
+ float A7 = A[0+a_off*3+1];
+
+ float k00,k11;
+
+ float *ee0 = e +i_off;
+ float *ee2 = ee0+k_off;
+
+ for (i=n; i > 0; --i) {
+ k00 = ee0[ 0] - ee2[ 0];
+ k11 = ee0[-1] - ee2[-1];
+ ee0[ 0] = ee0[ 0] + ee2[ 0];
+ ee0[-1] = ee0[-1] + ee2[-1];
+ ee2[ 0] = (k00) * A0 - (k11) * A1;
+ ee2[-1] = (k11) * A0 + (k00) * A1;
+
+ k00 = ee0[-2] - ee2[-2];
+ k11 = ee0[-3] - ee2[-3];
+ ee0[-2] = ee0[-2] + ee2[-2];
+ ee0[-3] = ee0[-3] + ee2[-3];
+ ee2[-2] = (k00) * A2 - (k11) * A3;
+ ee2[-3] = (k11) * A2 + (k00) * A3;
+
+ k00 = ee0[-4] - ee2[-4];
+ k11 = ee0[-5] - ee2[-5];
+ ee0[-4] = ee0[-4] + ee2[-4];
+ ee0[-5] = ee0[-5] + ee2[-5];
+ ee2[-4] = (k00) * A4 - (k11) * A5;
+ ee2[-5] = (k11) * A4 + (k00) * A5;
+
+ k00 = ee0[-6] - ee2[-6];
+ k11 = ee0[-7] - ee2[-7];
+ ee0[-6] = ee0[-6] + ee2[-6];
+ ee0[-7] = ee0[-7] + ee2[-7];
+ ee2[-6] = (k00) * A6 - (k11) * A7;
+ ee2[-7] = (k11) * A6 + (k00) * A7;
+
+ ee0 -= k0;
+ ee2 -= k0;
+ }
+}
+
+static __forceinline void iter_54(float *z)
+{
+ float k00,k11,k22,k33;
+ float y0,y1,y2,y3;
+
+ k00 = z[ 0] - z[-4];
+ y0 = z[ 0] + z[-4];
+ y2 = z[-2] + z[-6];
+ k22 = z[-2] - z[-6];
+
+ z[-0] = y0 + y2; // z0 + z4 + z2 + z6
+ z[-2] = y0 - y2; // z0 + z4 - z2 - z6
+
+ // done with y0,y2
+
+ k33 = z[-3] - z[-7];
+
+ z[-4] = k00 + k33; // z0 - z4 + z3 - z7
+ z[-6] = k00 - k33; // z0 - z4 - z3 + z7
+
+ // done with k33
+
+ k11 = z[-1] - z[-5];
+ y1 = z[-1] + z[-5];
+ y3 = z[-3] + z[-7];
+
+ z[-1] = y1 + y3; // z1 + z5 + z3 + z7
+ z[-3] = y1 - y3; // z1 + z5 - z3 - z7
+ z[-5] = k11 - k22; // z1 - z5 + z2 - z6
+ z[-7] = k11 + k22; // z1 - z5 - z2 + z6
+}
+
+static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n)
+{
+ int a_off = base_n >> 3;
+ float A2 = A[0+a_off];
+ float *z = e + i_off;
+ float *base = z - 16 * n;
+
+ while (z > base) {
+ float k00,k11;
+
+ k00 = z[-0] - z[-8];
+ k11 = z[-1] - z[-9];
+ z[-0] = z[-0] + z[-8];
+ z[-1] = z[-1] + z[-9];
+ z[-8] = k00;
+ z[-9] = k11 ;
+
+ k00 = z[ -2] - z[-10];
+ k11 = z[ -3] - z[-11];
+ z[ -2] = z[ -2] + z[-10];
+ z[ -3] = z[ -3] + z[-11];
+ z[-10] = (k00+k11) * A2;
+ z[-11] = (k11-k00) * A2;
+
+ k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation
+ k11 = z[ -5] - z[-13];
+ z[ -4] = z[ -4] + z[-12];
+ z[ -5] = z[ -5] + z[-13];
+ z[-12] = k11;
+ z[-13] = k00;
+
+ k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation
+ k11 = z[ -7] - z[-15];
+ z[ -6] = z[ -6] + z[-14];
+ z[ -7] = z[ -7] + z[-15];
+ z[-14] = (k00+k11) * A2;
+ z[-15] = (k00-k11) * A2;
+
+ iter_54(z);
+ iter_54(z-8);
+ z -= 16;
+ }
+}
+
+static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
+{
+ int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l;
+ int ld;
+ // @OPTIMIZE: reduce register pressure by using fewer variables?
+ int save_point = temp_alloc_save(f);
+ float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2));
+ float *u=NULL,*v=NULL;
+ // twiddle factors
+ float *A = f->A[blocktype];
+
+ // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio"
+ // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function.
+
+ // kernel from paper
+
+
+ // merged:
+ // copy and reflect spectral data
+ // step 0
+
+ // note that it turns out that the items added together during
+ // this step are, in fact, being added to themselves (as reflected
+ // by step 0). inexplicable inefficiency! this became obvious
+ // once I combined the passes.
+
+ // so there's a missing 'times 2' here (for adding X to itself).
+ // this propogates through linearly to the end, where the numbers
+ // are 1/2 too small, and need to be compensated for.
+
+ {
+ float *d,*e, *AA, *e_stop;
+ d = &buf2[n2-2];
+ AA = A;
+ e = &buffer[0];
+ e_stop = &buffer[n2];
+ while (e != e_stop) {
+ d[1] = (e[0] * AA[0] - e[2]*AA[1]);
+ d[0] = (e[0] * AA[1] + e[2]*AA[0]);
+ d -= 2;
+ AA += 2;
+ e += 4;
+ }
+
+ e = &buffer[n2-3];
+ while (d >= buf2) {
+ d[1] = (-e[2] * AA[0] - -e[0]*AA[1]);
+ d[0] = (-e[2] * AA[1] + -e[0]*AA[0]);
+ d -= 2;
+ AA += 2;
+ e -= 4;
+ }
+ }
+
+ // now we use symbolic names for these, so that we can
+ // possibly swap their meaning as we change which operations
+ // are in place
+
+ u = buffer;
+ v = buf2;
+
+ // step 2 (paper output is w, now u)
+ // this could be in place, but the data ends up in the wrong
+ // place... _somebody_'s got to swap it, so this is nominated
+ {
+ float *AA = &A[n2-8];
+ float *d0,*d1, *e0, *e1;
+
+ e0 = &v[n4];
+ e1 = &v[0];
+
+ d0 = &u[n4];
+ d1 = &u[0];
+
+ while (AA >= A) {
+ float v40_20, v41_21;
+
+ v41_21 = e0[1] - e1[1];
+ v40_20 = e0[0] - e1[0];
+ d0[1] = e0[1] + e1[1];
+ d0[0] = e0[0] + e1[0];
+ d1[1] = v41_21*AA[4] - v40_20*AA[5];
+ d1[0] = v40_20*AA[4] + v41_21*AA[5];
+
+ v41_21 = e0[3] - e1[3];
+ v40_20 = e0[2] - e1[2];
+ d0[3] = e0[3] + e1[3];
+ d0[2] = e0[2] + e1[2];
+ d1[3] = v41_21*AA[0] - v40_20*AA[1];
+ d1[2] = v40_20*AA[0] + v41_21*AA[1];
+
+ AA -= 8;
+
+ d0 += 4;
+ d1 += 4;
+ e0 += 4;
+ e1 += 4;
+ }
+ }
+
+ // step 3
+ ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+
+ // optimized step 3:
+
+ // the original step3 loop can be nested r inside s or s inside r;
+ // it's written originally as s inside r, but this is dumb when r
+ // iterates many times, and s few. So I have two copies of it and
+ // switch between them halfway.
+
+ // this is iteration 0 of step 3
+ imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A);
+ imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A);
+
+ // this is iteration 1 of step 3
+ imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16);
+ imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16);
+ imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16);
+ imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16);
+
+ l=2;
+ for (; l < (ld-3)>>1; ++l) {
+ int k0 = n >> (l+2), k0_2 = k0>>1;
+ int lim = 1 << (l+1);
+ int i;
+ for (i=0; i < lim; ++i)
+ imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3));
+ }
+
+ for (; l < ld-6; ++l) {
+ int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1;
+ int rlim = n >> (l+6), r;
+ int lim = 1 << (l+1);
+ int i_off;
+ float *A0 = A;
+ i_off = n2-1;
+ for (r=rlim; r > 0; --r) {
+ imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0);
+ A0 += k1*4;
+ i_off -= 8;
+ }
+ }
+
+ // iterations with count:
+ // ld-6,-5,-4 all interleaved together
+ // the big win comes from getting rid of needless flops
+ // due to the constants on pass 5 & 4 being all 1 and 0;
+ // combining them to be simultaneous to improve cache made little difference
+ imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n);
+
+ // output is u
+
+ // step 4, 5, and 6
+ // cannot be in-place because of step 5
+ {
+ uint16 *bitrev = f->bit_reverse[blocktype];
+ // weirdly, I'd have thought reading sequentially and writing
+ // erratically would have been better than vice-versa, but in
+ // fact that's not what my testing showed. (That is, with
+ // j = bitreverse(i), do you read i and write j, or read j and write i.)
+
+ float *d0 = &v[n4-4];
+ float *d1 = &v[n2-4];
+ while (d0 >= v) {
+ int k4;
+
+ k4 = bitrev[0];
+ d1[3] = u[k4+0];
+ d1[2] = u[k4+1];
+ d0[3] = u[k4+2];
+ d0[2] = u[k4+3];
+
+ k4 = bitrev[1];
+ d1[1] = u[k4+0];
+ d1[0] = u[k4+1];
+ d0[1] = u[k4+2];
+ d0[0] = u[k4+3];
+
+ d0 -= 4;
+ d1 -= 4;
+ bitrev += 2;
+ }
+ }
+ // (paper output is u, now v)
+
+
+ // data must be in buf2
+ assert(v == buf2);
+
+ // step 7 (paper output is v, now v)
+ // this is now in place
+ {
+ float *C = f->C[blocktype];
+ float *d, *e;
+
+ d = v;
+ e = v + n2 - 4;
+
+ while (d < e) {
+ float a02,a11,b0,b1,b2,b3;
+
+ a02 = d[0] - e[2];
+ a11 = d[1] + e[3];
+
+ b0 = C[1]*a02 + C[0]*a11;
+ b1 = C[1]*a11 - C[0]*a02;
+
+ b2 = d[0] + e[ 2];
+ b3 = d[1] - e[ 3];
+
+ d[0] = b2 + b0;
+ d[1] = b3 + b1;
+ e[2] = b2 - b0;
+ e[3] = b1 - b3;
+
+ a02 = d[2] - e[0];
+ a11 = d[3] + e[1];
+
+ b0 = C[3]*a02 + C[2]*a11;
+ b1 = C[3]*a11 - C[2]*a02;
+
+ b2 = d[2] + e[ 0];
+ b3 = d[3] - e[ 1];
+
+ d[2] = b2 + b0;
+ d[3] = b3 + b1;
+ e[0] = b2 - b0;
+ e[1] = b1 - b3;
+
+ C += 4;
+ d += 4;
+ e -= 4;
+ }
+ }
+
+ // data must be in buf2
+
+
+ // step 8+decode (paper output is X, now buffer)
+ // this generates pairs of data a la 8 and pushes them directly through
+ // the decode kernel (pushing rather than pulling) to avoid having
+ // to make another pass later
+
+ // this cannot POSSIBLY be in place, so we refer to the buffers directly
+
+ {
+ float *d0,*d1,*d2,*d3;
+
+ float *B = f->B[blocktype] + n2 - 8;
+ float *e = buf2 + n2 - 8;
+ d0 = &buffer[0];
+ d1 = &buffer[n2-4];
+ d2 = &buffer[n2];
+ d3 = &buffer[n-4];
+ while (e >= v) {
+ float p0,p1,p2,p3;
+
+ p3 = e[6]*B[7] - e[7]*B[6];
+ p2 = -e[6]*B[6] - e[7]*B[7];
+
+ d0[0] = p3;
+ d1[3] = - p3;
+ d2[0] = p2;
+ d3[3] = p2;
+
+ p1 = e[4]*B[5] - e[5]*B[4];
+ p0 = -e[4]*B[4] - e[5]*B[5];
+
+ d0[1] = p1;
+ d1[2] = - p1;
+ d2[1] = p0;
+ d3[2] = p0;
+
+ p3 = e[2]*B[3] - e[3]*B[2];
+ p2 = -e[2]*B[2] - e[3]*B[3];
+
+ d0[2] = p3;
+ d1[1] = - p3;
+ d2[2] = p2;
+ d3[1] = p2;
+
+ p1 = e[0]*B[1] - e[1]*B[0];
+ p0 = -e[0]*B[0] - e[1]*B[1];
+
+ d0[3] = p1;
+ d1[0] = - p1;
+ d2[3] = p0;
+ d3[0] = p0;
+
+ B -= 8;
+ e -= 8;
+ d0 += 4;
+ d2 += 4;
+ d1 -= 4;
+ d3 -= 4;
+ }
+ }
+
+ temp_free(f,buf2);
+ temp_alloc_restore(f,save_point);
+}
+
+#if 0
+// this is the original version of the above code, if you want to optimize it from scratch
+void inverse_mdct_naive(float *buffer, int n)
+{
+ float s;
+ float A[1 << 12], B[1 << 12], C[1 << 11];
+ int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l;
+ int n3_4 = n - n4, ld;
+ // how can they claim this only uses N words?!
+ // oh, because they're only used sparsely, whoops
+ float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13];
+ // set up twiddle factors
+
+ for (k=k2=0; k < n4; ++k,k2+=2) {
+ A[k2 ] = (float) cos(4*k*M_PI/n);
+ A[k2+1] = (float) -sin(4*k*M_PI/n);
+ B[k2 ] = (float) cos((k2+1)*M_PI/n/2);
+ B[k2+1] = (float) sin((k2+1)*M_PI/n/2);
+ }
+ for (k=k2=0; k < n8; ++k,k2+=2) {
+ C[k2 ] = (float) cos(2*(k2+1)*M_PI/n);
+ C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n);
+ }
+
+ // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio"
+ // Note there are bugs in that pseudocode, presumably due to them attempting
+ // to rename the arrays nicely rather than representing the way their actual
+ // implementation bounces buffers back and forth. As a result, even in the
+ // "some formulars corrected" version, a direct implementation fails. These
+ // are noted below as "paper bug".
+
+ // copy and reflect spectral data
+ for (k=0; k < n2; ++k) u[k] = buffer[k];
+ for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1];
+ // kernel from paper
+ // step 1
+ for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) {
+ v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1];
+ v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2];
+ }
+ // step 2
+ for (k=k4=0; k < n8; k+=1, k4+=4) {
+ w[n2+3+k4] = v[n2+3+k4] + v[k4+3];
+ w[n2+1+k4] = v[n2+1+k4] + v[k4+1];
+ w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4];
+ w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4];
+ }
+ // step 3
+ ld = ilog(n) - 1; // ilog is off-by-one from normal definitions
+ for (l=0; l < ld-3; ++l) {
+ int k0 = n >> (l+2), k1 = 1 << (l+3);
+ int rlim = n >> (l+4), r4, r;
+ int s2lim = 1 << (l+2), s2;
+ for (r=r4=0; r < rlim; r4+=4,++r) {
+ for (s2=0; s2 < s2lim; s2+=2) {
+ u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4];
+ u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4];
+ u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1]
+ - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1];
+ u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1]
+ + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1];
+ }
+ }
+ if (l+1 < ld-3) {
+ // paper bug: ping-ponging of u&w here is omitted
+ memcpy(w, u, sizeof(u));
+ }
+ }
+
+ // step 4
+ for (i=0; i < n8; ++i) {
+ int j = bit_reverse(i) >> (32-ld+3);
+ assert(j < n8);
+ if (i == j) {
+ // paper bug: original code probably swapped in place; if copying,
+ // need to directly copy in this case
+ int i8 = i << 3;
+ v[i8+1] = u[i8+1];
+ v[i8+3] = u[i8+3];
+ v[i8+5] = u[i8+5];
+ v[i8+7] = u[i8+7];
+ } else if (i < j) {
+ int i8 = i << 3, j8 = j << 3;
+ v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1];
+ v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3];
+ v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5];
+ v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7];
+ }
+ }
+ // step 5
+ for (k=0; k < n2; ++k) {
+ w[k] = v[k*2+1];
+ }
+ // step 6
+ for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) {
+ u[n-1-k2] = w[k4];
+ u[n-2-k2] = w[k4+1];
+ u[n3_4 - 1 - k2] = w[k4+2];
+ u[n3_4 - 2 - k2] = w[k4+3];
+ }
+ // step 7
+ for (k=k2=0; k < n8; ++k, k2 += 2) {
+ v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2;
+ v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2;
+ v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2;
+ v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2;
+ }
+ // step 8
+ for (k=k2=0; k < n4; ++k,k2 += 2) {
+ X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1];
+ X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ];
+ }
+
+ // decode kernel to output
+ // determined the following value experimentally
+ // (by first figuring out what made inverse_mdct_slow work); then matching that here
+ // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?)
+ s = 0.5; // theoretically would be n4
+
+ // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code,
+ // so it needs to use the "old" B values to behave correctly, or else
+ // set s to 1.0 ]]]
+ for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4];
+ for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1];
+ for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4];
+}
+#endif
+
+static float *get_window(vorb *f, int len)
+{
+ len <<= 1;
+ if (len == f->blocksize_0) return f->window[0];
+ if (len == f->blocksize_1) return f->window[1];
+ assert(0);
+ return NULL;
+}
+
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+typedef int16 YTYPE;
+#else
+typedef int YTYPE;
+#endif
+static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag)
+{
+ int n2 = n >> 1;
+ int s = map->chan[i].mux, floor;
+ floor = map->submap_floor[s];
+ if (f->floor_types[floor] == 0) {
+ return error(f, VORBIS_invalid_stream);
+ } else {
+ Floor1 *g = &f->floor_config[floor].floor1;
+ int j,q;
+ int lx = 0, ly = finalY[0] * g->floor1_multiplier;
+ for (q=1; q < g->values; ++q) {
+ j = g->sorted_order[q];
+ #ifndef STB_VORBIS_NO_DEFER_FLOOR
+ if (finalY[j] >= 0)
+ #else
+ if (step2_flag[j])
+ #endif
+ {
+ int hy = finalY[j] * g->floor1_multiplier;
+ int hx = g->Xlist[j];
+ if (lx != hx)
+ draw_line(target, lx,ly, hx,hy, n2);
+ CHECK(f);
+ lx = hx, ly = hy;
+ }
+ }
+ if (lx < n2) {
+ // optimization of: draw_line(target, lx,ly, n,ly, n2);
+ for (j=lx; j < n2; ++j)
+ LINE_OP(target[j], inverse_db_table[ly]);
+ CHECK(f);
+ }
+ }
+ return TRUE;
+}
+
+// The meaning of "left" and "right"
+//
+// For a given frame:
+// we compute samples from 0..n
+// window_center is n/2
+// we'll window and mix the samples from left_start to left_end with data from the previous frame
+// all of the samples from left_end to right_start can be output without mixing; however,
+// this interval is 0-length except when transitioning between short and long frames
+// all of the samples from right_start to right_end need to be mixed with the next frame,
+// which we don't have, so those get saved in a buffer
+// frame N's right_end-right_start, the number of samples to mix with the next frame,
+// has to be the same as frame N+1's left_end-left_start (which they are by
+// construction)
+
+static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
+{
+ Mode *m;
+ int i, n, prev, next, window_center;
+ f->channel_buffer_start = f->channel_buffer_end = 0;
+
+ retry:
+ if (f->eof) return FALSE;
+ if (!maybe_start_packet(f))
+ return FALSE;
+ // check packet type
+ if (get_bits(f,1) != 0) {
+ if (IS_PUSH_MODE(f))
+ return error(f,VORBIS_bad_packet_type);
+ while (EOP != get8_packet(f));
+ goto retry;
+ }
+
+ if (f->alloc.alloc_buffer)
+ assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+
+ i = get_bits(f, ilog(f->mode_count-1));
+ if (i == EOP) return FALSE;
+ if (i >= f->mode_count) return FALSE;
+ *mode = i;
+ m = f->mode_config + i;
+ if (m->blockflag) {
+ n = f->blocksize_1;
+ prev = get_bits(f,1);
+ next = get_bits(f,1);
+ } else {
+ prev = next = 0;
+ n = f->blocksize_0;
+ }
+
+// WINDOWING
+
+ window_center = n >> 1;
+ if (m->blockflag && !prev) {
+ *p_left_start = (n - f->blocksize_0) >> 2;
+ *p_left_end = (n + f->blocksize_0) >> 2;
+ } else {
+ *p_left_start = 0;
+ *p_left_end = window_center;
+ }
+ if (m->blockflag && !next) {
+ *p_right_start = (n*3 - f->blocksize_0) >> 2;
+ *p_right_end = (n*3 + f->blocksize_0) >> 2;
+ } else {
+ *p_right_start = window_center;
+ *p_right_end = n;
+ }
+
+ return TRUE;
+}
+
+static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left)
+{
+ Mapping *map;
+ int i,j,k,n,n2;
+ int zero_channel[256];
+ int really_zero_channel[256];
+
+// WINDOWING
+
+ n = f->blocksize[m->blockflag];
+ map = &f->mapping[m->mapping];
+
+// FLOORS
+ n2 = n >> 1;
+
+ CHECK(f);
+
+ for (i=0; i < f->channels; ++i) {
+ int s = map->chan[i].mux, floor;
+ zero_channel[i] = FALSE;
+ floor = map->submap_floor[s];
+ if (f->floor_types[floor] == 0) {
+ return error(f, VORBIS_invalid_stream);
+ } else {
+ Floor1 *g = &f->floor_config[floor].floor1;
+ if (get_bits(f, 1)) {
+ short *finalY;
+ uint8 step2_flag[256];
+ static int range_list[4] = { 256, 128, 86, 64 };
+ int range = range_list[g->floor1_multiplier-1];
+ int offset = 2;
+ finalY = f->finalY[i];
+ finalY[0] = get_bits(f, ilog(range)-1);
+ finalY[1] = get_bits(f, ilog(range)-1);
+ for (j=0; j < g->partitions; ++j) {
+ int pclass = g->partition_class_list[j];
+ int cdim = g->class_dimensions[pclass];
+ int cbits = g->class_subclasses[pclass];
+ int csub = (1 << cbits)-1;
+ int cval = 0;
+ if (cbits) {
+ Codebook *c = f->codebooks + g->class_masterbooks[pclass];
+ DECODE(cval,f,c);
+ }
+ for (k=0; k < cdim; ++k) {
+ int book = g->subclass_books[pclass][cval & csub];
+ cval = cval >> cbits;
+ if (book >= 0) {
+ int temp;
+ Codebook *c = f->codebooks + book;
+ DECODE(temp,f,c);
+ finalY[offset++] = temp;
+ } else
+ finalY[offset++] = 0;
+ }
+ }
+ if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec
+ step2_flag[0] = step2_flag[1] = 1;
+ for (j=2; j < g->values; ++j) {
+ int low, high, pred, highroom, lowroom, room, val;
+ low = g->neighbors[j][0];
+ high = g->neighbors[j][1];
+ //neighbors(g->Xlist, j, &low, &high);
+ pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]);
+ val = finalY[j];
+ highroom = range - pred;
+ lowroom = pred;
+ if (highroom < lowroom)
+ room = highroom * 2;
+ else
+ room = lowroom * 2;
+ if (val) {
+ step2_flag[low] = step2_flag[high] = 1;
+ step2_flag[j] = 1;
+ if (val >= room)
+ if (highroom > lowroom)
+ finalY[j] = val - lowroom + pred;
+ else
+ finalY[j] = pred - val + highroom - 1;
+ else
+ if (val & 1)
+ finalY[j] = pred - ((val+1)>>1);
+ else
+ finalY[j] = pred + (val>>1);
+ } else {
+ step2_flag[j] = 0;
+ finalY[j] = pred;
+ }
+ }
+
+#ifdef STB_VORBIS_NO_DEFER_FLOOR
+ do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag);
+#else
+ // defer final floor computation until _after_ residue
+ for (j=0; j < g->values; ++j) {
+ if (!step2_flag[j])
+ finalY[j] = -1;
+ }
+#endif
+ } else {
+ error:
+ zero_channel[i] = TRUE;
+ }
+ // So we just defer everything else to later
+
+ // at this point we've decoded the floor into buffer
+ }
+ }
+ CHECK(f);
+ // at this point we've decoded all floors
+
+ if (f->alloc.alloc_buffer)
+ assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+
+ // re-enable coupled channels if necessary
+ memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels);
+ for (i=0; i < map->coupling_steps; ++i)
+ if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) {
+ zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE;
+ }
+
+ CHECK(f);
+// RESIDUE DECODE
+ for (i=0; i < map->submaps; ++i) {
+ float *residue_buffers[STB_VORBIS_MAX_CHANNELS];
+ int r;
+ uint8 do_not_decode[256];
+ int ch = 0;
+ for (j=0; j < f->channels; ++j) {
+ if (map->chan[j].mux == i) {
+ if (zero_channel[j]) {
+ do_not_decode[ch] = TRUE;
+ residue_buffers[ch] = NULL;
+ } else {
+ do_not_decode[ch] = FALSE;
+ residue_buffers[ch] = f->channel_buffers[j];
+ }
+ ++ch;
+ }
+ }
+ r = map->submap_residue[i];
+ decode_residue(f, residue_buffers, ch, n2, r, do_not_decode);
+ }
+
+ if (f->alloc.alloc_buffer)
+ assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+ CHECK(f);
+
+// INVERSE COUPLING
+ for (i = map->coupling_steps-1; i >= 0; --i) {
+ int n2 = n >> 1;
+ float *m = f->channel_buffers[map->chan[i].magnitude];
+ float *a = f->channel_buffers[map->chan[i].angle ];
+ for (j=0; j < n2; ++j) {
+ float a2,m2;
+ if (m[j] > 0)
+ if (a[j] > 0)
+ m2 = m[j], a2 = m[j] - a[j];
+ else
+ a2 = m[j], m2 = m[j] + a[j];
+ else
+ if (a[j] > 0)
+ m2 = m[j], a2 = m[j] + a[j];
+ else
+ a2 = m[j], m2 = m[j] - a[j];
+ m[j] = m2;
+ a[j] = a2;
+ }
+ }
+ CHECK(f);
+
+ // finish decoding the floors
+#ifndef STB_VORBIS_NO_DEFER_FLOOR
+ for (i=0; i < f->channels; ++i) {
+ if (really_zero_channel[i]) {
+ memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
+ } else {
+ do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL);
+ }
+ }
+#else
+ for (i=0; i < f->channels; ++i) {
+ if (really_zero_channel[i]) {
+ memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2);
+ } else {
+ for (j=0; j < n2; ++j)
+ f->channel_buffers[i][j] *= f->floor_buffers[i][j];
+ }
+ }
+#endif
+
+// INVERSE MDCT
+ CHECK(f);
+ for (i=0; i < f->channels; ++i)
+ inverse_mdct(f->channel_buffers[i], n, f, m->blockflag);
+ CHECK(f);
+
+ // this shouldn't be necessary, unless we exited on an error
+ // and want to flush to get to the next packet
+ flush_packet(f);
+
+ if (f->first_decode) {
+ // assume we start so first non-discarded sample is sample 0
+ // this isn't to spec, but spec would require us to read ahead
+ // and decode the size of all current frames--could be done,
+ // but presumably it's not a commonly used feature
+ f->current_loc = -n2; // start of first frame is positioned for discard
+ // we might have to discard samples "from" the next frame too,
+ // if we're lapping a large block then a small at the start?
+ f->discard_samples_deferred = n - right_end;
+ f->current_loc_valid = TRUE;
+ f->first_decode = FALSE;
+ } else if (f->discard_samples_deferred) {
+ if (f->discard_samples_deferred >= right_start - left_start) {
+ f->discard_samples_deferred -= (right_start - left_start);
+ left_start = right_start;
+ *p_left = left_start;
+ } else {
+ left_start += f->discard_samples_deferred;
+ *p_left = left_start;
+ f->discard_samples_deferred = 0;
+ }
+ } else if (f->previous_length == 0 && f->current_loc_valid) {
+ // we're recovering from a seek... that means we're going to discard
+ // the samples from this packet even though we know our position from
+ // the last page header, so we need to update the position based on
+ // the discarded samples here
+ // but wait, the code below is going to add this in itself even
+ // on a discard, so we don't need to do it here...
+ }
+
+ // check if we have ogg information about the sample # for this packet
+ if (f->last_seg_which == f->end_seg_with_known_loc) {
+ // if we have a valid current loc, and this is final:
+ if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) {
+ uint32 current_end = f->known_loc_for_packet;
+ // then let's infer the size of the (probably) short final frame
+ if (current_end < f->current_loc + (right_end-left_start)) {
+ if (current_end < f->current_loc) {
+ // negative truncation, that's impossible!
+ *len = 0;
+ } else {
+ *len = current_end - f->current_loc;
+ }
+ *len += left_start; // this doesn't seem right, but has no ill effect on my test files
+ if (*len > right_end) *len = right_end; // this should never happen
+ f->current_loc += *len;
+ return TRUE;
+ }
+ }
+ // otherwise, just set our sample loc
+ // guess that the ogg granule pos refers to the _middle_ of the
+ // last frame?
+ // set f->current_loc to the position of left_start
+ f->current_loc = f->known_loc_for_packet - (n2-left_start);
+ f->current_loc_valid = TRUE;
+ }
+ if (f->current_loc_valid)
+ f->current_loc += (right_start - left_start);
+
+ if (f->alloc.alloc_buffer)
+ assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset);
+ *len = right_end; // ignore samples after the window goes to 0
+ CHECK(f);
+
+ return TRUE;
+}
+
+static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right)
+{
+ int mode, left_end, right_end;
+ if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0;
+ return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left);
+}
+
+static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right)
+{
+ int prev,i,j;
+ // we use right&left (the start of the right- and left-window sin()-regions)
+ // to determine how much to return, rather than inferring from the rules
+ // (same result, clearer code); 'left' indicates where our sin() window
+ // starts, therefore where the previous window's right edge starts, and
+ // therefore where to start mixing from the previous buffer. 'right'
+ // indicates where our sin() ending-window starts, therefore that's where
+ // we start saving, and where our returned-data ends.
+
+ // mixin from previous window
+ if (f->previous_length) {
+ int i,j, n = f->previous_length;
+ float *w = get_window(f, n);
+ for (i=0; i < f->channels; ++i) {
+ for (j=0; j < n; ++j)
+ f->channel_buffers[i][left+j] =
+ f->channel_buffers[i][left+j]*w[ j] +
+ f->previous_window[i][ j]*w[n-1-j];
+ }
+ }
+
+ prev = f->previous_length;
+
+ // last half of this data becomes previous window
+ f->previous_length = len - right;
+
+ // @OPTIMIZE: could avoid this copy by double-buffering the
+ // output (flipping previous_window with channel_buffers), but
+ // then previous_window would have to be 2x as large, and
+ // channel_buffers couldn't be temp mem (although they're NOT
+ // currently temp mem, they could be (unless we want to level
+ // performance by spreading out the computation))
+ for (i=0; i < f->channels; ++i)
+ for (j=0; right+j < len; ++j)
+ f->previous_window[i][j] = f->channel_buffers[i][right+j];
+
+ if (!prev)
+ // there was no previous packet, so this data isn't valid...
+ // this isn't entirely true, only the would-have-overlapped data
+ // isn't valid, but this seems to be what the spec requires
+ return 0;
+
+ // truncate a short frame
+ if (len < right) right = len;
+
+ f->samples_output += right-left;
+
+ return right - left;
+}
+
+static int vorbis_pump_first_frame(stb_vorbis *f)
+{
+ int len, right, left, res;
+ res = vorbis_decode_packet(f, &len, &left, &right);
+ if (res)
+ vorbis_finish_frame(f, len, left, right);
+ return res;
+}
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+static int is_whole_packet_present(stb_vorbis *f, int end_page)
+{
+ // make sure that we have the packet available before continuing...
+ // this requires a full ogg parse, but we know we can fetch from f->stream
+
+ // instead of coding this out explicitly, we could save the current read state,
+ // read the next packet with get8() until end-of-packet, check f->eof, then
+ // reset the state? but that would be slower, esp. since we'd have over 256 bytes
+ // of state to restore (primarily the page segment table)
+
+ int s = f->next_seg, first = TRUE;
+ uint8 *p = f->stream;
+
+ if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag
+ for (; s < f->segment_count; ++s) {
+ p += f->segments[s];
+ if (f->segments[s] < 255) // stop at first short segment
+ break;
+ }
+ // either this continues, or it ends it...
+ if (end_page)
+ if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream);
+ if (s == f->segment_count)
+ s = -1; // set 'crosses page' flag
+ if (p > f->stream_end) return error(f, VORBIS_need_more_data);
+ first = FALSE;
+ }
+ for (; s == -1;) {
+ uint8 *q;
+ int n;
+
+ // check that we have the page header ready
+ if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data);
+ // validate the page
+ if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream);
+ if (p[4] != 0) return error(f, VORBIS_invalid_stream);
+ if (first) { // the first segment must NOT have 'continued_packet', later ones MUST
+ if (f->previous_length)
+ if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream);
+ // if no previous length, we're resynching, so we can come in on a continued-packet,
+ // which we'll just drop
+ } else {
+ if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream);
+ }
+ n = p[26]; // segment counts
+ q = p+27; // q points to segment table
+ p = q + n; // advance past header
+ // make sure we've read the segment table
+ if (p > f->stream_end) return error(f, VORBIS_need_more_data);
+ for (s=0; s < n; ++s) {
+ p += q[s];
+ if (q[s] < 255)
+ break;
+ }
+ if (end_page)
+ if (s < n-1) return error(f, VORBIS_invalid_stream);
+ if (s == n)
+ s = -1; // set 'crosses page' flag
+ if (p > f->stream_end) return error(f, VORBIS_need_more_data);
+ first = FALSE;
+ }
+ return TRUE;
+}
+#endif // !STB_VORBIS_NO_PUSHDATA_API
+
+static int start_decoder(vorb *f)
+{
+ uint8 header[6], x,y;
+ int len,i,j,k, max_submaps = 0;
+ int longest_floorlist=0;
+
+ // first page, first packet
+
+ if (!start_page(f)) return FALSE;
+ // validate page flag
+ if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page);
+ if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page);
+ if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page);
+ // check for expected packet length
+ if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page);
+ if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page);
+ // read packet
+ // check packet header
+ if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page);
+ if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof);
+ if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page);
+ // vorbis_version
+ if (get32(f) != 0) return error(f, VORBIS_invalid_first_page);
+ f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page);
+ if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels);
+ f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page);
+ get32(f); // bitrate_maximum
+ get32(f); // bitrate_nominal
+ get32(f); // bitrate_minimum
+ x = get8(f);
+ {
+ int log0,log1;
+ log0 = x & 15;
+ log1 = x >> 4;
+ f->blocksize_0 = 1 << log0;
+ f->blocksize_1 = 1 << log1;
+ if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup);
+ if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup);
+ if (log0 > log1) return error(f, VORBIS_invalid_setup);
+ }
+
+ // framing_flag
+ x = get8(f);
+ if (!(x & 1)) return error(f, VORBIS_invalid_first_page);
+
+ // second packet!
+ if (!start_page(f)) return FALSE;
+
+ if (!start_packet(f)) return FALSE;
+ do {
+ len = next_segment(f);
+ skip(f, len);
+ f->bytes_in_seg = 0;
+ } while (len);
+
+ // third packet!
+ if (!start_packet(f)) return FALSE;
+
+ #ifndef STB_VORBIS_NO_PUSHDATA_API
+ if (IS_PUSH_MODE(f)) {
+ if (!is_whole_packet_present(f, TRUE)) {
+ // convert error in ogg header to write type
+ if (f->error == VORBIS_invalid_stream)
+ f->error = VORBIS_invalid_setup;
+ return FALSE;
+ }
+ }
+ #endif
+
+ crc32_init(); // always init it, to avoid multithread race conditions
+
+ if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup);
+ for (i=0; i < 6; ++i) header[i] = get8_packet(f);
+ if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup);
+
+ // codebooks
+
+ f->codebook_count = get_bits(f,8) + 1;
+ f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count);
+ if (f->codebooks == NULL) return error(f, VORBIS_outofmem);
+ memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count);
+ for (i=0; i < f->codebook_count; ++i) {
+ uint32 *values;
+ int ordered, sorted_count;
+ int total=0;
+ uint8 *lengths;
+ Codebook *c = f->codebooks+i;
+ CHECK(f);
+ x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup);
+ x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup);
+ x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup);
+ x = get_bits(f, 8);
+ c->dimensions = (get_bits(f, 8)<<8) + x;
+ x = get_bits(f, 8);
+ y = get_bits(f, 8);
+ c->entries = (get_bits(f, 8)<<16) + (y<<8) + x;
+ ordered = get_bits(f,1);
+ c->sparse = ordered ? 0 : get_bits(f,1);
+
+ if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup);
+
+ if (c->sparse)
+ lengths = (uint8 *) setup_temp_malloc(f, c->entries);
+ else
+ lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
+
+ if (!lengths) return error(f, VORBIS_outofmem);
+
+ if (ordered) {
+ int current_entry = 0;
+ int current_length = get_bits(f,5) + 1;
+ while (current_entry < c->entries) {
+ int limit = c->entries - current_entry;
+ int n = get_bits(f, ilog(limit));
+ if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); }
+ memset(lengths + current_entry, current_length, n);
+ current_entry += n;
+ ++current_length;
+ }
+ } else {
+ for (j=0; j < c->entries; ++j) {
+ int present = c->sparse ? get_bits(f,1) : 1;
+ if (present) {
+ lengths[j] = get_bits(f, 5) + 1;
+ ++total;
+ if (lengths[j] == 32)
+ return error(f, VORBIS_invalid_setup);
+ } else {
+ lengths[j] = NO_CODE;
+ }
+ }
+ }
+
+ if (c->sparse && total >= c->entries >> 2) {
+ // convert sparse items to non-sparse!
+ if (c->entries > (int) f->setup_temp_memory_required)
+ f->setup_temp_memory_required = c->entries;
+
+ c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries);
+ if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem);
+ memcpy(c->codeword_lengths, lengths, c->entries);
+ setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs!
+ lengths = c->codeword_lengths;
+ c->sparse = 0;
+ }
+
+ // compute the size of the sorted tables
+ if (c->sparse) {
+ sorted_count = total;
+ } else {
+ sorted_count = 0;
+ #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH
+ for (j=0; j < c->entries; ++j)
+ if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE)
+ ++sorted_count;
+ #endif
+ }
+
+ c->sorted_entries = sorted_count;
+ values = NULL;
+
+ CHECK(f);
+ if (!c->sparse) {
+ c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries);
+ if (!c->codewords) return error(f, VORBIS_outofmem);
+ } else {
+ unsigned int size;
+ if (c->sorted_entries) {
+ c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries);
+ if (!c->codeword_lengths) return error(f, VORBIS_outofmem);
+ c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries);
+ if (!c->codewords) return error(f, VORBIS_outofmem);
+ values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries);
+ if (!values) return error(f, VORBIS_outofmem);
+ }
+ size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries;
+ if (size > f->setup_temp_memory_required)
+ f->setup_temp_memory_required = size;
+ }
+
+ if (!compute_codewords(c, lengths, c->entries, values)) {
+ if (c->sparse) setup_temp_free(f, values, 0);
+ return error(f, VORBIS_invalid_setup);
+ }
+
+ if (c->sorted_entries) {
+ // allocate an extra slot for sentinels
+ c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1));
+ if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem);
+ // allocate an extra slot at the front so that c->sorted_values[-1] is defined
+ // so that we can catch that case without an extra if
+ c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1));
+ if (c->sorted_values == NULL) return error(f, VORBIS_outofmem);
+ ++c->sorted_values;
+ c->sorted_values[-1] = -1;
+ compute_sorted_huffman(c, lengths, values);
+ }
+
+ if (c->sparse) {
+ setup_temp_free(f, values, sizeof(*values)*c->sorted_entries);
+ setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries);
+ setup_temp_free(f, lengths, c->entries);
+ c->codewords = NULL;
+ }
+
+ compute_accelerated_huffman(c);
+
+ CHECK(f);
+ c->lookup_type = get_bits(f, 4);
+ if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup);
+ if (c->lookup_type > 0) {
+ uint16 *mults;
+ c->minimum_value = float32_unpack(get_bits(f, 32));
+ c->delta_value = float32_unpack(get_bits(f, 32));
+ c->value_bits = get_bits(f, 4)+1;
+ c->sequence_p = get_bits(f,1);
+ if (c->lookup_type == 1) {
+ c->lookup_values = lookup1_values(c->entries, c->dimensions);
+ } else {
+ c->lookup_values = c->entries * c->dimensions;
+ }
+ if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup);
+ mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values);
+ if (mults == NULL) return error(f, VORBIS_outofmem);
+ for (j=0; j < (int) c->lookup_values; ++j) {
+ int q = get_bits(f, c->value_bits);
+ if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); }
+ mults[j] = q;
+ }
+
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ if (c->lookup_type == 1) {
+ int len, sparse = c->sparse;
+ float last=0;
+ // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop
+ if (sparse) {
+ if (c->sorted_entries == 0) goto skip;
+ c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions);
+ } else
+ c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions);
+ if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
+ len = sparse ? c->sorted_entries : c->entries;
+ for (j=0; j < len; ++j) {
+ unsigned int z = sparse ? c->sorted_values[j] : j;
+ unsigned int div=1;
+ for (k=0; k < c->dimensions; ++k) {
+ int off = (z / div) % c->lookup_values;
+ float val = mults[off];
+ val = mults[off]*c->delta_value + c->minimum_value + last;
+ c->multiplicands[j*c->dimensions + k] = val;
+ if (c->sequence_p)
+ last = val;
+ if (k+1 < c->dimensions) {
+ if (div > UINT_MAX / (unsigned int) c->lookup_values) {
+ setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
+ return error(f, VORBIS_invalid_setup);
+ }
+ div *= c->lookup_values;
+ }
+ }
+ }
+ c->lookup_type = 2;
+ }
+ else
+#endif
+ {
+ float last=0;
+ CHECK(f);
+ c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values);
+ if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); }
+ for (j=0; j < (int) c->lookup_values; ++j) {
+ float val = mults[j] * c->delta_value + c->minimum_value + last;
+ c->multiplicands[j] = val;
+ if (c->sequence_p)
+ last = val;
+ }
+ }
+#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
+ skip:;
+#endif
+ setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values);
+
+ CHECK(f);
+ }
+ CHECK(f);
+ }
+
+ // time domain transfers (notused)
+
+ x = get_bits(f, 6) + 1;
+ for (i=0; i < x; ++i) {
+ uint32 z = get_bits(f, 16);
+ if (z != 0) return error(f, VORBIS_invalid_setup);
+ }
+
+ // Floors
+ f->floor_count = get_bits(f, 6)+1;
+ f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config));
+ if (f->floor_config == NULL) return error(f, VORBIS_outofmem);
+ for (i=0; i < f->floor_count; ++i) {
+ f->floor_types[i] = get_bits(f, 16);
+ if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup);
+ if (f->floor_types[i] == 0) {
+ Floor0 *g = &f->floor_config[i].floor0;
+ g->order = get_bits(f,8);
+ g->rate = get_bits(f,16);
+ g->bark_map_size = get_bits(f,16);
+ g->amplitude_bits = get_bits(f,6);
+ g->amplitude_offset = get_bits(f,8);
+ g->number_of_books = get_bits(f,4) + 1;
+ for (j=0; j < g->number_of_books; ++j)
+ g->book_list[j] = get_bits(f,8);
+ return error(f, VORBIS_feature_not_supported);
+ } else {
+ stbv__floor_ordering p[31*8+2];
+ Floor1 *g = &f->floor_config[i].floor1;
+ int max_class = -1;
+ g->partitions = get_bits(f, 5);
+ for (j=0; j < g->partitions; ++j) {
+ g->partition_class_list[j] = get_bits(f, 4);
+ if (g->partition_class_list[j] > max_class)
+ max_class = g->partition_class_list[j];
+ }
+ for (j=0; j <= max_class; ++j) {
+ g->class_dimensions[j] = get_bits(f, 3)+1;
+ g->class_subclasses[j] = get_bits(f, 2);
+ if (g->class_subclasses[j]) {
+ g->class_masterbooks[j] = get_bits(f, 8);
+ if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+ }
+ for (k=0; k < 1 << g->class_subclasses[j]; ++k) {
+ g->subclass_books[j][k] = get_bits(f,8)-1;
+ if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+ }
+ }
+ g->floor1_multiplier = get_bits(f,2)+1;
+ g->rangebits = get_bits(f,4);
+ g->Xlist[0] = 0;
+ g->Xlist[1] = 1 << g->rangebits;
+ g->values = 2;
+ for (j=0; j < g->partitions; ++j) {
+ int c = g->partition_class_list[j];
+ for (k=0; k < g->class_dimensions[c]; ++k) {
+ g->Xlist[g->values] = get_bits(f, g->rangebits);
+ ++g->values;
+ }
+ }
+ // precompute the sorting
+ for (j=0; j < g->values; ++j) {
+ p[j].x = g->Xlist[j];
+ p[j].id = j;
+ }
+ qsort(p, g->values, sizeof(p[0]), point_compare);
+ for (j=0; j < g->values; ++j)
+ g->sorted_order[j] = (uint8) p[j].id;
+ // precompute the neighbors
+ for (j=2; j < g->values; ++j) {
+ int low,hi;
+ neighbors(g->Xlist, j, &low,&hi);
+ g->neighbors[j][0] = low;
+ g->neighbors[j][1] = hi;
+ }
+
+ if (g->values > longest_floorlist)
+ longest_floorlist = g->values;
+ }
+ }
+
+ // Residue
+ f->residue_count = get_bits(f, 6)+1;
+ f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0]));
+ if (f->residue_config == NULL) return error(f, VORBIS_outofmem);
+ memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0]));
+ for (i=0; i < f->residue_count; ++i) {
+ uint8 residue_cascade[64];
+ Residue *r = f->residue_config+i;
+ f->residue_types[i] = get_bits(f, 16);
+ if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup);
+ r->begin = get_bits(f, 24);
+ r->end = get_bits(f, 24);
+ if (r->end < r->begin) return error(f, VORBIS_invalid_setup);
+ r->part_size = get_bits(f,24)+1;
+ r->classifications = get_bits(f,6)+1;
+ r->classbook = get_bits(f,8);
+ if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+ for (j=0; j < r->classifications; ++j) {
+ uint8 high_bits=0;
+ uint8 low_bits=get_bits(f,3);
+ if (get_bits(f,1))
+ high_bits = get_bits(f,5);
+ residue_cascade[j] = high_bits*8 + low_bits;
+ }
+ r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications);
+ if (r->residue_books == NULL) return error(f, VORBIS_outofmem);
+ for (j=0; j < r->classifications; ++j) {
+ for (k=0; k < 8; ++k) {
+ if (residue_cascade[j] & (1 << k)) {
+ r->residue_books[j][k] = get_bits(f, 8);
+ if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup);
+ } else {
+ r->residue_books[j][k] = -1;
+ }
+ }
+ }
+ // precompute the classifications[] array to avoid inner-loop mod/divide
+ // call it 'classdata' since we already have r->classifications
+ r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries);
+ if (!r->classdata) return error(f, VORBIS_outofmem);
+ memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries);
+ for (j=0; j < f->codebooks[r->classbook].entries; ++j) {
+ int classwords = f->codebooks[r->classbook].dimensions;
+ int temp = j;
+ r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords);
+ if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem);
+ for (k=classwords-1; k >= 0; --k) {
+ r->classdata[j][k] = temp % r->classifications;
+ temp /= r->classifications;
+ }
+ }
+ }
+
+ f->mapping_count = get_bits(f,6)+1;
+ f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping));
+ if (f->mapping == NULL) return error(f, VORBIS_outofmem);
+ memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping));
+ for (i=0; i < f->mapping_count; ++i) {
+ Mapping *m = f->mapping + i;
+ int mapping_type = get_bits(f,16);
+ if (mapping_type != 0) return error(f, VORBIS_invalid_setup);
+ m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan));
+ if (m->chan == NULL) return error(f, VORBIS_outofmem);
+ if (get_bits(f,1))
+ m->submaps = get_bits(f,4)+1;
+ else
+ m->submaps = 1;
+ if (m->submaps > max_submaps)
+ max_submaps = m->submaps;
+ if (get_bits(f,1)) {
+ m->coupling_steps = get_bits(f,8)+1;
+ for (k=0; k < m->coupling_steps; ++k) {
+ m->chan[k].magnitude = get_bits(f, ilog(f->channels-1));
+ m->chan[k].angle = get_bits(f, ilog(f->channels-1));
+ if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup);
+ if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup);
+ if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup);
+ }
+ } else
+ m->coupling_steps = 0;
+
+ // reserved field
+ if (get_bits(f,2)) return error(f, VORBIS_invalid_setup);
+ if (m->submaps > 1) {
+ for (j=0; j < f->channels; ++j) {
+ m->chan[j].mux = get_bits(f, 4);
+ if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup);
+ }
+ } else
+ // @SPECIFICATION: this case is missing from the spec
+ for (j=0; j < f->channels; ++j)
+ m->chan[j].mux = 0;
+
+ for (j=0; j < m->submaps; ++j) {
+ get_bits(f,8); // discard
+ m->submap_floor[j] = get_bits(f,8);
+ m->submap_residue[j] = get_bits(f,8);
+ if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup);
+ if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup);
+ }
+ }
+
+ // Modes
+ f->mode_count = get_bits(f, 6)+1;
+ for (i=0; i < f->mode_count; ++i) {
+ Mode *m = f->mode_config+i;
+ m->blockflag = get_bits(f,1);
+ m->windowtype = get_bits(f,16);
+ m->transformtype = get_bits(f,16);
+ m->mapping = get_bits(f,8);
+ if (m->windowtype != 0) return error(f, VORBIS_invalid_setup);
+ if (m->transformtype != 0) return error(f, VORBIS_invalid_setup);
+ if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup);
+ }
+
+ flush_packet(f);
+
+ f->previous_length = 0;
+
+ for (i=0; i < f->channels; ++i) {
+ f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1);
+ f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
+ f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist);
+ if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem);
+ memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1);
+ #ifdef STB_VORBIS_NO_DEFER_FLOOR
+ f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2);
+ if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem);
+ #endif
+ }
+
+ if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE;
+ if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE;
+ f->blocksize[0] = f->blocksize_0;
+ f->blocksize[1] = f->blocksize_1;
+
+#ifdef STB_VORBIS_DIVIDE_TABLE
+ if (integer_divide_table[1][1]==0)
+ for (i=0; i < DIVTAB_NUMER; ++i)
+ for (j=1; j < DIVTAB_DENOM; ++j)
+ integer_divide_table[i][j] = i / j;
+#endif
+
+ // compute how much temporary memory is needed
+
+ // 1.
+ {
+ uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1);
+ uint32 classify_mem;
+ int i,max_part_read=0;
+ for (i=0; i < f->residue_count; ++i) {
+ Residue *r = f->residue_config + i;
+ unsigned int actual_size = f->blocksize_1 / 2;
+ unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size;
+ unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size;
+ int n_read = limit_r_end - limit_r_begin;
+ int part_read = n_read / r->part_size;
+ if (part_read > max_part_read)
+ max_part_read = part_read;
+ }
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *));
+ #else
+ classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *));
+ #endif
+
+ // maximum reasonable partition size is f->blocksize_1
+
+ f->temp_memory_required = classify_mem;
+ if (imdct_mem > f->temp_memory_required)
+ f->temp_memory_required = imdct_mem;
+ }
+
+ f->first_decode = TRUE;
+
+ if (f->alloc.alloc_buffer) {
+ assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes);
+ // check if there's enough temp memory so we don't error later
+ if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset)
+ return error(f, VORBIS_outofmem);
+ }
+
+ f->first_audio_page_offset = stb_vorbis_get_file_offset(f);
+
+ return TRUE;
+}
+
+static void vorbis_deinit(stb_vorbis *p)
+{
+ int i,j;
+ if (p->residue_config) {
+ for (i=0; i < p->residue_count; ++i) {
+ Residue *r = p->residue_config+i;
+ if (r->classdata) {
+ for (j=0; j < p->codebooks[r->classbook].entries; ++j)
+ setup_free(p, r->classdata[j]);
+ setup_free(p, r->classdata);
+ }
+ setup_free(p, r->residue_books);
+ }
+ }
+
+ if (p->codebooks) {
+ CHECK(p);
+ for (i=0; i < p->codebook_count; ++i) {
+ Codebook *c = p->codebooks + i;
+ setup_free(p, c->codeword_lengths);
+ setup_free(p, c->multiplicands);
+ setup_free(p, c->codewords);
+ setup_free(p, c->sorted_codewords);
+ // c->sorted_values[-1] is the first entry in the array
+ setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL);
+ }
+ setup_free(p, p->codebooks);
+ }
+ setup_free(p, p->floor_config);
+ setup_free(p, p->residue_config);
+ if (p->mapping) {
+ for (i=0; i < p->mapping_count; ++i)
+ setup_free(p, p->mapping[i].chan);
+ setup_free(p, p->mapping);
+ }
+ CHECK(p);
+ for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) {
+ setup_free(p, p->channel_buffers[i]);
+ setup_free(p, p->previous_window[i]);
+ #ifdef STB_VORBIS_NO_DEFER_FLOOR
+ setup_free(p, p->floor_buffers[i]);
+ #endif
+ setup_free(p, p->finalY[i]);
+ }
+ for (i=0; i < 2; ++i) {
+ setup_free(p, p->A[i]);
+ setup_free(p, p->B[i]);
+ setup_free(p, p->C[i]);
+ setup_free(p, p->window[i]);
+ setup_free(p, p->bit_reverse[i]);
+ }
+ #ifndef STB_VORBIS_NO_STDIO
+ if (p->close_on_free) fclose(p->f);
+ #endif
+}
+
+void stb_vorbis_close(stb_vorbis *p)
+{
+ if (p == NULL) return;
+ vorbis_deinit(p);
+ setup_free(p,p);
+}
+
+static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z)
+{
+ memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start
+ if (z) {
+ p->alloc = *z;
+ p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3;
+ p->temp_offset = p->alloc.alloc_buffer_length_in_bytes;
+ }
+ p->eof = 0;
+ p->error = VORBIS__no_error;
+ p->stream = NULL;
+ p->codebooks = NULL;
+ p->page_crc_tests = -1;
+ #ifndef STB_VORBIS_NO_STDIO
+ p->close_on_free = FALSE;
+ p->f = NULL;
+ #endif
+}
+
+int stb_vorbis_get_sample_offset(stb_vorbis *f)
+{
+ if (f->current_loc_valid)
+ return f->current_loc;
+ else
+ return -1;
+}
+
+stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f)
+{
+ stb_vorbis_info d;
+ d.channels = f->channels;
+ d.sample_rate = f->sample_rate;
+ d.setup_memory_required = f->setup_memory_required;
+ d.setup_temp_memory_required = f->setup_temp_memory_required;
+ d.temp_memory_required = f->temp_memory_required;
+ d.max_frame_size = f->blocksize_1 >> 1;
+ return d;
+}
+
+int stb_vorbis_get_error(stb_vorbis *f)
+{
+ int e = f->error;
+ f->error = VORBIS__no_error;
+ return e;
+}
+
+static stb_vorbis * vorbis_alloc(stb_vorbis *f)
+{
+ stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p));
+ return p;
+}
+
+#ifndef STB_VORBIS_NO_PUSHDATA_API
+
+void stb_vorbis_flush_pushdata(stb_vorbis *f)
+{
+ f->previous_length = 0;
+ f->page_crc_tests = 0;
+ f->discard_samples_deferred = 0;
+ f->current_loc_valid = FALSE;
+ f->first_decode = FALSE;
+ f->samples_output = 0;
+ f->channel_buffer_start = 0;
+ f->channel_buffer_end = 0;
+}
+
+static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len)
+{
+ int i,n;
+ for (i=0; i < f->page_crc_tests; ++i)
+ f->scan[i].bytes_done = 0;
+
+ // if we have room for more scans, search for them first, because
+ // they may cause us to stop early if their header is incomplete
+ if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) {
+ if (data_len < 4) return 0;
+ data_len -= 3; // need to look for 4-byte sequence, so don't miss
+ // one that straddles a boundary
+ for (i=0; i < data_len; ++i) {
+ if (data[i] == 0x4f) {
+ if (0==memcmp(data+i, ogg_page_header, 4)) {
+ int j,len;
+ uint32 crc;
+ // make sure we have the whole page header
+ if (i+26 >= data_len || i+27+data[i+26] >= data_len) {
+ // only read up to this page start, so hopefully we'll
+ // have the whole page header start next time
+ data_len = i;
+ break;
+ }
+ // ok, we have it all; compute the length of the page
+ len = 27 + data[i+26];
+ for (j=0; j < data[i+26]; ++j)
+ len += data[i+27+j];
+ // scan everything up to the embedded crc (which we must 0)
+ crc = 0;
+ for (j=0; j < 22; ++j)
+ crc = crc32_update(crc, data[i+j]);
+ // now process 4 0-bytes
+ for ( ; j < 26; ++j)
+ crc = crc32_update(crc, 0);
+ // len is the total number of bytes we need to scan
+ n = f->page_crc_tests++;
+ f->scan[n].bytes_left = len-j;
+ f->scan[n].crc_so_far = crc;
+ f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24);
+ // if the last frame on a page is continued to the next, then
+ // we can't recover the sample_loc immediately
+ if (data[i+27+data[i+26]-1] == 255)
+ f->scan[n].sample_loc = ~0;
+ else
+ f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24);
+ f->scan[n].bytes_done = i+j;
+ if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT)
+ break;
+ // keep going if we still have room for more
+ }
+ }
+ }
+ }
+
+ for (i=0; i < f->page_crc_tests;) {
+ uint32 crc;
+ int j;
+ int n = f->scan[i].bytes_done;
+ int m = f->scan[i].bytes_left;
+ if (m > data_len - n) m = data_len - n;
+ // m is the bytes to scan in the current chunk
+ crc = f->scan[i].crc_so_far;
+ for (j=0; j < m; ++j)
+ crc = crc32_update(crc, data[n+j]);
+ f->scan[i].bytes_left -= m;
+ f->scan[i].crc_so_far = crc;
+ if (f->scan[i].bytes_left == 0) {
+ // does it match?
+ if (f->scan[i].crc_so_far == f->scan[i].goal_crc) {
+ // Houston, we have page
+ data_len = n+m; // consumption amount is wherever that scan ended
+ f->page_crc_tests = -1; // drop out of page scan mode
+ f->previous_length = 0; // decode-but-don't-output one frame
+ f->next_seg = -1; // start a new page
+ f->current_loc = f->scan[i].sample_loc; // set the current sample location
+ // to the amount we'd have decoded had we decoded this page
+ f->current_loc_valid = f->current_loc != ~0U;
+ return data_len;
+ }
+ // delete entry
+ f->scan[i] = f->scan[--f->page_crc_tests];
+ } else {
+ ++i;
+ }
+ }
+
+ return data_len;
+}
+
+// return value: number of bytes we used
+int stb_vorbis_decode_frame_pushdata(
+ stb_vorbis *f, // the file we're decoding
+ const uint8 *data, int data_len, // the memory available for decoding
+ int *channels, // place to write number of float * buffers
+ float ***output, // place to write float ** array of float * buffers
+ int *samples // place to write number of output samples
+ )
+{
+ int i;
+ int len,right,left;
+
+ if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+ if (f->page_crc_tests >= 0) {
+ *samples = 0;
+ return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len);
+ }
+
+ f->stream = (uint8 *) data;
+ f->stream_end = (uint8 *) data + data_len;
+ f->error = VORBIS__no_error;
+
+ // check that we have the entire packet in memory
+ if (!is_whole_packet_present(f, FALSE)) {
+ *samples = 0;
+ return 0;
+ }
+
+ if (!vorbis_decode_packet(f, &len, &left, &right)) {
+ // save the actual error we encountered
+ enum STBVorbisError error = f->error;
+ if (error == VORBIS_bad_packet_type) {
+ // flush and resynch
+ f->error = VORBIS__no_error;
+ while (get8_packet(f) != EOP)
+ if (f->eof) break;
+ *samples = 0;
+ return (int) (f->stream - data);
+ }
+ if (error == VORBIS_continued_packet_flag_invalid) {
+ if (f->previous_length == 0) {
+ // we may be resynching, in which case it's ok to hit one
+ // of these; just discard the packet
+ f->error = VORBIS__no_error;
+ while (get8_packet(f) != EOP)
+ if (f->eof) break;
+ *samples = 0;
+ return (int) (f->stream - data);
+ }
+ }
+ // if we get an error while parsing, what to do?
+ // well, it DEFINITELY won't work to continue from where we are!
+ stb_vorbis_flush_pushdata(f);
+ // restore the error that actually made us bail
+ f->error = error;
+ *samples = 0;
+ return 1;
+ }
+
+ // success!
+ len = vorbis_finish_frame(f, len, left, right);
+ for (i=0; i < f->channels; ++i)
+ f->outputs[i] = f->channel_buffers[i] + left;
+
+ if (channels) *channels = f->channels;
+ *samples = len;
+ *output = f->outputs;
+ return (int) (f->stream - data);
+}
+
+stb_vorbis *stb_vorbis_open_pushdata(
+ const unsigned char *data, int data_len, // the memory available for decoding
+ int *data_used, // only defined if result is not NULL
+ int *error, const stb_vorbis_alloc *alloc)
+{
+ stb_vorbis *f, p;
+ vorbis_init(&p, alloc);
+ p.stream = (uint8 *) data;
+ p.stream_end = (uint8 *) data + data_len;
+ p.push_mode = TRUE;
+ if (!start_decoder(&p)) {
+ if (p.eof)
+ *error = VORBIS_need_more_data;
+ else
+ *error = p.error;
+ return NULL;
+ }
+ f = vorbis_alloc(&p);
+ if (f) {
+ *f = p;
+ *data_used = (int) (f->stream - data);
+ *error = 0;
+ return f;
+ } else {
+ vorbis_deinit(&p);
+ return NULL;
+ }
+}
+#endif // STB_VORBIS_NO_PUSHDATA_API
+
+unsigned int stb_vorbis_get_file_offset(stb_vorbis *f)
+{
+ #ifndef STB_VORBIS_NO_PUSHDATA_API
+ if (f->push_mode) return 0;
+ #endif
+ if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start);
+ #ifndef STB_VORBIS_NO_STDIO
+ return (unsigned int) (ftell(f->f) - f->f_start);
+ #endif
+}
+
+#ifndef STB_VORBIS_NO_PULLDATA_API
+//
+// DATA-PULLING API
+//
+
+static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last)
+{
+ for(;;) {
+ int n;
+ if (f->eof) return 0;
+ n = get8(f);
+ if (n == 0x4f) { // page header candidate
+ unsigned int retry_loc = stb_vorbis_get_file_offset(f);
+ int i;
+ // check if we're off the end of a file_section stream
+ if (retry_loc - 25 > f->stream_len)
+ return 0;
+ // check the rest of the header
+ for (i=1; i < 4; ++i)
+ if (get8(f) != ogg_page_header[i])
+ break;
+ if (f->eof) return 0;
+ if (i == 4) {
+ uint8 header[27];
+ uint32 i, crc, goal, len;
+ for (i=0; i < 4; ++i)
+ header[i] = ogg_page_header[i];
+ for (; i < 27; ++i)
+ header[i] = get8(f);
+ if (f->eof) return 0;
+ if (header[4] != 0) goto invalid;
+ goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24);
+ for (i=22; i < 26; ++i)
+ header[i] = 0;
+ crc = 0;
+ for (i=0; i < 27; ++i)
+ crc = crc32_update(crc, header[i]);
+ len = 0;
+ for (i=0; i < header[26]; ++i) {
+ int s = get8(f);
+ crc = crc32_update(crc, s);
+ len += s;
+ }
+ if (len && f->eof) return 0;
+ for (i=0; i < len; ++i)
+ crc = crc32_update(crc, get8(f));
+ // finished parsing probable page
+ if (crc == goal) {
+ // we could now check that it's either got the last
+ // page flag set, OR it's followed by the capture
+ // pattern, but I guess TECHNICALLY you could have
+ // a file with garbage between each ogg page and recover
+ // from it automatically? So even though that paranoia
+ // might decrease the chance of an invalid decode by
+ // another 2^32, not worth it since it would hose those
+ // invalid-but-useful files?
+ if (end)
+ *end = stb_vorbis_get_file_offset(f);
+ if (last) {
+ if (header[5] & 0x04)
+ *last = 1;
+ else
+ *last = 0;
+ }
+ set_file_offset(f, retry_loc-1);
+ return 1;
+ }
+ }
+ invalid:
+ // not a valid page, so rewind and look for next one
+ set_file_offset(f, retry_loc);
+ }
+ }
+}
+
+
+#define SAMPLE_unknown 0xffffffff
+
+// seeking is implemented with a binary search, which narrows down the range to
+// 64K, before using a linear search (because finding the synchronization
+// pattern can be expensive, and the chance we'd find the end page again is
+// relatively high for small ranges)
+//
+// two initial interpolation-style probes are used at the start of the search
+// to try to bound either side of the binary search sensibly, while still
+// working in O(log n) time if they fail.
+
+static int get_seek_page_info(stb_vorbis *f, ProbedPage *z)
+{
+ uint8 header[27], lacing[255];
+ int i,len;
+
+ // record where the page starts
+ z->page_start = stb_vorbis_get_file_offset(f);
+
+ // parse the header
+ getn(f, header, 27);
+ if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S')
+ return 0;
+ getn(f, lacing, header[26]);
+
+ // determine the length of the payload
+ len = 0;
+ for (i=0; i < header[26]; ++i)
+ len += lacing[i];
+
+ // this implies where the page ends
+ z->page_end = z->page_start + 27 + header[26] + len;
+
+ // read the last-decoded sample out of the data
+ z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24);
+
+ // restore file state to where we were
+ set_file_offset(f, z->page_start);
+ return 1;
+}
+
+// rarely used function to seek back to the preceeding page while finding the
+// start of a packet
+static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset)
+{
+ unsigned int previous_safe, end;
+
+ // now we want to seek back 64K from the limit
+ if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset)
+ previous_safe = limit_offset - 65536;
+ else
+ previous_safe = f->first_audio_page_offset;
+
+ set_file_offset(f, previous_safe);
+
+ while (vorbis_find_page(f, &end, NULL)) {
+ if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset)
+ return 1;
+ set_file_offset(f, end);
+ }
+
+ return 0;
+}
+
+// implements the search logic for finding a page and starting decoding. if
+// the function succeeds, current_loc_valid will be true and current_loc will
+// be less than or equal to the provided sample number (the closer the
+// better).
+static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number)
+{
+ ProbedPage left, right, mid;
+ int i, start_seg_with_known_loc, end_pos, page_start;
+ uint32 delta, stream_length, padding;
+ double offset, bytes_per_sample;
+ int probe = 0;
+
+ // find the last page and validate the target sample
+ stream_length = stb_vorbis_stream_length_in_samples(f);
+ if (stream_length == 0) return error(f, VORBIS_seek_without_length);
+ if (sample_number > stream_length) return error(f, VORBIS_seek_invalid);
+
+ // this is the maximum difference between the window-center (which is the
+ // actual granule position value), and the right-start (which the spec
+ // indicates should be the granule position (give or take one)).
+ padding = ((f->blocksize_1 - f->blocksize_0) >> 2);
+ if (sample_number < padding)
+ sample_number = 0;
+ else
+ sample_number -= padding;
+
+ left = f->p_first;
+ while (left.last_decoded_sample == ~0U) {
+ // (untested) the first page does not have a 'last_decoded_sample'
+ set_file_offset(f, left.page_end);
+ if (!get_seek_page_info(f, &left)) goto error;
+ }
+
+ right = f->p_last;
+ assert(right.last_decoded_sample != ~0U);
+
+ // starting from the start is handled differently
+ if (sample_number <= left.last_decoded_sample) {
+ if (stb_vorbis_seek_start(f))
+ return 1;
+ return 0;
+ }
+
+ while (left.page_end != right.page_start) {
+ assert(left.page_end < right.page_start);
+ // search range in bytes
+ delta = right.page_start - left.page_end;
+ if (delta <= 65536) {
+ // there's only 64K left to search - handle it linearly
+ set_file_offset(f, left.page_end);
+ } else {
+ if (probe < 2) {
+ if (probe == 0) {
+ // first probe (interpolate)
+ double data_bytes = right.page_end - left.page_start;
+ bytes_per_sample = data_bytes / right.last_decoded_sample;
+ offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample);
+ } else {
+ // second probe (try to bound the other side)
+ double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample;
+ if (error >= 0 && error < 8000) error = 8000;
+ if (error < 0 && error > -8000) error = -8000;
+ offset += error * 2;
+ }
+
+ // ensure the offset is valid
+ if (offset < left.page_end)
+ offset = left.page_end;
+ if (offset > right.page_start - 65536)
+ offset = right.page_start - 65536;
+
+ set_file_offset(f, (unsigned int) offset);
+ } else {
+ // binary search for large ranges (offset by 32K to ensure
+ // we don't hit the right page)
+ set_file_offset(f, left.page_end + (delta / 2) - 32768);
+ }
+
+ if (!vorbis_find_page(f, NULL, NULL)) goto error;
+ }
+
+ for (;;) {
+ if (!get_seek_page_info(f, &mid)) goto error;
+ if (mid.last_decoded_sample != ~0U) break;
+ // (untested) no frames end on this page
+ set_file_offset(f, mid.page_end);
+ assert(mid.page_start < right.page_start);
+ }
+
+ // if we've just found the last page again then we're in a tricky file,
+ // and we're close enough.
+ if (mid.page_start == right.page_start)
+ break;
+
+ if (sample_number < mid.last_decoded_sample)
+ right = mid;
+ else
+ left = mid;
+
+ ++probe;
+ }
+
+ // seek back to start of the last packet
+ page_start = left.page_start;
+ set_file_offset(f, page_start);
+ if (!start_page(f)) return error(f, VORBIS_seek_failed);
+ end_pos = f->end_seg_with_known_loc;
+ assert(end_pos >= 0);
+
+ for (;;) {
+ for (i = end_pos; i > 0; --i)
+ if (f->segments[i-1] != 255)
+ break;
+
+ start_seg_with_known_loc = i;
+
+ if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet))
+ break;
+
+ // (untested) the final packet begins on an earlier page
+ if (!go_to_page_before(f, page_start))
+ goto error;
+
+ page_start = stb_vorbis_get_file_offset(f);
+ if (!start_page(f)) goto error;
+ end_pos = f->segment_count - 1;
+ }
+
+ // prepare to start decoding
+ f->current_loc_valid = FALSE;
+ f->last_seg = FALSE;
+ f->valid_bits = 0;
+ f->packet_bytes = 0;
+ f->bytes_in_seg = 0;
+ f->previous_length = 0;
+ f->next_seg = start_seg_with_known_loc;
+
+ for (i = 0; i < start_seg_with_known_loc; i++)
+ skip(f, f->segments[i]);
+
+ // start decoding (optimizable - this frame is generally discarded)
+ if (!vorbis_pump_first_frame(f))
+ return 0;
+ if (f->current_loc > sample_number)
+ return error(f, VORBIS_seek_failed);
+ return 1;
+
+error:
+ // try to restore the file to a valid state
+ stb_vorbis_seek_start(f);
+ return error(f, VORBIS_seek_failed);
+}
+
+// the same as vorbis_decode_initial, but without advancing
+static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode)
+{
+ int bits_read, bytes_read;
+
+ if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode))
+ return 0;
+
+ // either 1 or 2 bytes were read, figure out which so we can rewind
+ bits_read = 1 + ilog(f->mode_count-1);
+ if (f->mode_config[*mode].blockflag)
+ bits_read += 2;
+ bytes_read = (bits_read + 7) / 8;
+
+ f->bytes_in_seg += bytes_read;
+ f->packet_bytes -= bytes_read;
+ skip(f, -bytes_read);
+ if (f->next_seg == -1)
+ f->next_seg = f->segment_count - 1;
+ else
+ f->next_seg--;
+ f->valid_bits = 0;
+
+ return 1;
+}
+
+int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number)
+{
+ uint32 max_frame_samples;
+
+ if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+ // fast page-level search
+ if (!seek_to_sample_coarse(f, sample_number))
+ return 0;
+
+ assert(f->current_loc_valid);
+ assert(f->current_loc <= sample_number);
+
+ // linear search for the relevant packet
+ max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2;
+ while (f->current_loc < sample_number) {
+ int left_start, left_end, right_start, right_end, mode, frame_samples;
+ if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode))
+ return error(f, VORBIS_seek_failed);
+ // calculate the number of samples returned by the next frame
+ frame_samples = right_start - left_start;
+ if (f->current_loc + frame_samples > sample_number) {
+ return 1; // the next frame will contain the sample
+ } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) {
+ // there's a chance the frame after this could contain the sample
+ vorbis_pump_first_frame(f);
+ } else {
+ // this frame is too early to be relevant
+ f->current_loc += frame_samples;
+ f->previous_length = 0;
+ maybe_start_packet(f);
+ flush_packet(f);
+ }
+ }
+ // the next frame will start with the sample
+ assert(f->current_loc == sample_number);
+ return 1;
+}
+
+int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number)
+{
+ if (!stb_vorbis_seek_frame(f, sample_number))
+ return 0;
+
+ if (sample_number != f->current_loc) {
+ int n;
+ uint32 frame_start = f->current_loc;
+ stb_vorbis_get_frame_float(f, &n, NULL);
+ assert(sample_number > frame_start);
+ assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end);
+ f->channel_buffer_start += (sample_number - frame_start);
+ }
+
+ return 1;
+}
+
+int stb_vorbis_seek_start(stb_vorbis *f)
+{
+ if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); }
+ set_file_offset(f, f->first_audio_page_offset);
+ f->previous_length = 0;
+ f->first_decode = TRUE;
+ f->next_seg = -1;
+ return vorbis_pump_first_frame(f);
+}
+
+unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f)
+{
+ unsigned int restore_offset, previous_safe;
+ unsigned int end, last_page_loc;
+
+ if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+ if (!f->total_samples) {
+ unsigned int last;
+ uint32 lo,hi;
+ char header[6];
+
+ // first, store the current decode position so we can restore it
+ restore_offset = stb_vorbis_get_file_offset(f);
+
+ // now we want to seek back 64K from the end (the last page must
+ // be at most a little less than 64K, but let's allow a little slop)
+ if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset)
+ previous_safe = f->stream_len - 65536;
+ else
+ previous_safe = f->first_audio_page_offset;
+
+ set_file_offset(f, previous_safe);
+ // previous_safe is now our candidate 'earliest known place that seeking
+ // to will lead to the final page'
+
+ if (!vorbis_find_page(f, &end, &last)) {
+ // if we can't find a page, we're hosed!
+ f->error = VORBIS_cant_find_last_page;
+ f->total_samples = 0xffffffff;
+ goto done;
+ }
+
+ // check if there are more pages
+ last_page_loc = stb_vorbis_get_file_offset(f);
+
+ // stop when the last_page flag is set, not when we reach eof;
+ // this allows us to stop short of a 'file_section' end without
+ // explicitly checking the length of the section
+ while (!last) {
+ set_file_offset(f, end);
+ if (!vorbis_find_page(f, &end, &last)) {
+ // the last page we found didn't have the 'last page' flag
+ // set. whoops!
+ break;
+ }
+ previous_safe = last_page_loc+1;
+ last_page_loc = stb_vorbis_get_file_offset(f);
+ }
+
+ set_file_offset(f, last_page_loc);
+
+ // parse the header
+ getn(f, (unsigned char *)header, 6);
+ // extract the absolute granule position
+ lo = get32(f);
+ hi = get32(f);
+ if (lo == 0xffffffff && hi == 0xffffffff) {
+ f->error = VORBIS_cant_find_last_page;
+ f->total_samples = SAMPLE_unknown;
+ goto done;
+ }
+ if (hi)
+ lo = 0xfffffffe; // saturate
+ f->total_samples = lo;
+
+ f->p_last.page_start = last_page_loc;
+ f->p_last.page_end = end;
+ f->p_last.last_decoded_sample = lo;
+
+ done:
+ set_file_offset(f, restore_offset);
+ }
+ return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples;
+}
+
+float stb_vorbis_stream_length_in_seconds(stb_vorbis *f)
+{
+ return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate;
+}
+
+
+
+int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output)
+{
+ int len, right,left,i;
+ if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing);
+
+ if (!vorbis_decode_packet(f, &len, &left, &right)) {
+ f->channel_buffer_start = f->channel_buffer_end = 0;
+ return 0;
+ }
+
+ len = vorbis_finish_frame(f, len, left, right);
+ for (i=0; i < f->channels; ++i)
+ f->outputs[i] = f->channel_buffers[i] + left;
+
+ f->channel_buffer_start = left;
+ f->channel_buffer_end = left+len;
+
+ if (channels) *channels = f->channels;
+ if (output) *output = f->outputs;
+ return len;
+}
+
+#ifndef STB_VORBIS_NO_STDIO
+
+stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length)
+{
+ stb_vorbis *f, p;
+ vorbis_init(&p, alloc);
+ p.f = file;
+ p.f_start = (uint32) ftell(file);
+ p.stream_len = length;
+ p.close_on_free = close_on_free;
+ if (start_decoder(&p)) {
+ f = vorbis_alloc(&p);
+ if (f) {
+ *f = p;
+ vorbis_pump_first_frame(f);
+ return f;
+ }
+ }
+ if (error) *error = p.error;
+ vorbis_deinit(&p);
+ return NULL;
+}
+
+stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc)
+{
+ unsigned int len, start;
+ start = (unsigned int) ftell(file);
+ fseek(file, 0, SEEK_END);
+ len = (unsigned int) (ftell(file) - start);
+ fseek(file, start, SEEK_SET);
+ return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len);
+}
+
+stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc)
+{
+ FILE *f = fopen(filename, "rb");
+ if (f)
+ return stb_vorbis_open_file(f, TRUE, error, alloc);
+ if (error) *error = VORBIS_file_open_failure;
+ return NULL;
+}
+#endif // STB_VORBIS_NO_STDIO
+
+stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc)
+{
+ stb_vorbis *f, p;
+ if (data == NULL) return NULL;
+ vorbis_init(&p, alloc);
+ p.stream = (uint8 *) data;
+ p.stream_end = (uint8 *) data + len;
+ p.stream_start = (uint8 *) p.stream;
+ p.stream_len = len;
+ p.push_mode = FALSE;
+ if (start_decoder(&p)) {
+ f = vorbis_alloc(&p);
+ if (f) {
+ *f = p;
+ vorbis_pump_first_frame(f);
+ if (error) *error = VORBIS__no_error;
+ return f;
+ }
+ }
+ if (error) *error = p.error;
+ vorbis_deinit(&p);
+ return NULL;
+}
+
+#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+#define PLAYBACK_MONO 1
+#define PLAYBACK_LEFT 2
+#define PLAYBACK_RIGHT 4
+
+#define L (PLAYBACK_LEFT | PLAYBACK_MONO)
+#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO)
+#define R (PLAYBACK_RIGHT | PLAYBACK_MONO)
+
+static int8 channel_position[7][6] =
+{
+ { 0 },
+ { C },
+ { L, R },
+ { L, C, R },
+ { L, R, L, R },
+ { L, C, R, L, R },
+ { L, C, R, L, R, C },
+};
+
+
+#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT
+ typedef union {
+ float f;
+ int i;
+ } float_conv;
+ typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4];
+ #define FASTDEF(x) float_conv x
+ // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round
+ #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT))
+ #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22))
+ #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s))
+ #define check_endianness()
+#else
+ #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s))))
+ #define check_endianness()
+ #define FASTDEF(x)
+#endif
+
+static void copy_samples(short *dest, float *src, int len)
+{
+ int i;
+ check_endianness();
+ for (i=0; i < len; ++i) {
+ FASTDEF(temp);
+ int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15);
+ if ((unsigned int) (v + 32768) > 65535)
+ v = v < 0 ? -32768 : 32767;
+ dest[i] = v;
+ }
+}
+
+static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len)
+{
+ #define BUFFER_SIZE 32
+ float buffer[BUFFER_SIZE];
+ int i,j,o,n = BUFFER_SIZE;
+ check_endianness();
+ for (o = 0; o < len; o += BUFFER_SIZE) {
+ memset(buffer, 0, sizeof(buffer));
+ if (o + n > len) n = len - o;
+ for (j=0; j < num_c; ++j) {
+ if (channel_position[num_c][j] & mask) {
+ for (i=0; i < n; ++i)
+ buffer[i] += data[j][d_offset+o+i];
+ }
+ }
+ for (i=0; i < n; ++i) {
+ FASTDEF(temp);
+ int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
+ if ((unsigned int) (v + 32768) > 65535)
+ v = v < 0 ? -32768 : 32767;
+ output[o+i] = v;
+ }
+ }
+}
+
+static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len)
+{
+ #define BUFFER_SIZE 32
+ float buffer[BUFFER_SIZE];
+ int i,j,o,n = BUFFER_SIZE >> 1;
+ // o is the offset in the source data
+ check_endianness();
+ for (o = 0; o < len; o += BUFFER_SIZE >> 1) {
+ // o2 is the offset in the output data
+ int o2 = o << 1;
+ memset(buffer, 0, sizeof(buffer));
+ if (o + n > len) n = len - o;
+ for (j=0; j < num_c; ++j) {
+ int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT);
+ if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) {
+ for (i=0; i < n; ++i) {
+ buffer[i*2+0] += data[j][d_offset+o+i];
+ buffer[i*2+1] += data[j][d_offset+o+i];
+ }
+ } else if (m == PLAYBACK_LEFT) {
+ for (i=0; i < n; ++i) {
+ buffer[i*2+0] += data[j][d_offset+o+i];
+ }
+ } else if (m == PLAYBACK_RIGHT) {
+ for (i=0; i < n; ++i) {
+ buffer[i*2+1] += data[j][d_offset+o+i];
+ }
+ }
+ }
+ for (i=0; i < (n<<1); ++i) {
+ FASTDEF(temp);
+ int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15);
+ if ((unsigned int) (v + 32768) > 65535)
+ v = v < 0 ? -32768 : 32767;
+ output[o2+i] = v;
+ }
+ }
+}
+
+static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples)
+{
+ int i;
+ if (buf_c != data_c && buf_c <= 2 && data_c <= 6) {
+ static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} };
+ for (i=0; i < buf_c; ++i)
+ compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples);
+ } else {
+ int limit = buf_c < data_c ? buf_c : data_c;
+ for (i=0; i < limit; ++i)
+ copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples);
+ for ( ; i < buf_c; ++i)
+ memset(buffer[i]+b_offset, 0, sizeof(short) * samples);
+ }
+}
+
+int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples)
+{
+ float **output;
+ int len = stb_vorbis_get_frame_float(f, NULL, &output);
+ if (len > num_samples) len = num_samples;
+ if (len)
+ convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len);
+ return len;
+}
+
+static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len)
+{
+ int i;
+ check_endianness();
+ if (buf_c != data_c && buf_c <= 2 && data_c <= 6) {
+ assert(buf_c == 2);
+ for (i=0; i < buf_c; ++i)
+ compute_stereo_samples(buffer, data_c, data, d_offset, len);
+ } else {
+ int limit = buf_c < data_c ? buf_c : data_c;
+ int j;
+ for (j=0; j < len; ++j) {
+ for (i=0; i < limit; ++i) {
+ FASTDEF(temp);
+ float f = data[i][d_offset+j];
+ int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15);
+ if ((unsigned int) (v + 32768) > 65535)
+ v = v < 0 ? -32768 : 32767;
+ *buffer++ = v;
+ }
+ for ( ; i < buf_c; ++i)
+ *buffer++ = 0;
+ }
+ }
+}
+
+int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts)
+{
+ float **output;
+ int len;
+ if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts);
+ len = stb_vorbis_get_frame_float(f, NULL, &output);
+ if (len) {
+ if (len*num_c > num_shorts) len = num_shorts / num_c;
+ convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len);
+ }
+ return len;
+}
+
+int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts)
+{
+ float **outputs;
+ int len = num_shorts / channels;
+ int n=0;
+ int z = f->channels;
+ if (z > channels) z = channels;
+ while (n < len) {
+ int k = f->channel_buffer_end - f->channel_buffer_start;
+ if (n+k >= len) k = len - n;
+ if (k)
+ convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k);
+ buffer += k*channels;
+ n += k;
+ f->channel_buffer_start += k;
+ if (n == len) break;
+ if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break;
+ }
+ return n;
+}
+
+int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len)
+{
+ float **outputs;
+ int n=0;
+ int z = f->channels;
+ if (z > channels) z = channels;
+ while (n < len) {
+ int k = f->channel_buffer_end - f->channel_buffer_start;
+ if (n+k >= len) k = len - n;
+ if (k)
+ convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k);
+ n += k;
+ f->channel_buffer_start += k;
+ if (n == len) break;
+ if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break;
+ }
+ return n;
+}
+
+#ifndef STB_VORBIS_NO_STDIO
+int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output)
+{
+ int data_len, offset, total, limit, error;
+ short *data;
+ stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
+ if (v == NULL) return -1;
+ limit = v->channels * 4096;
+ *channels = v->channels;
+ if (sample_rate)
+ *sample_rate = v->sample_rate;
+ offset = data_len = 0;
+ total = limit;
+ data = (short *) malloc(total * sizeof(*data));
+ if (data == NULL) {
+ stb_vorbis_close(v);
+ return -2;
+ }
+ for (;;) {
+ int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset);
+ if (n == 0) break;
+ data_len += n;
+ offset += n * v->channels;
+ if (offset + limit > total) {
+ short *data2;
+ total *= 2;
+ data2 = (short *) realloc(data, total * sizeof(*data));
+ if (data2 == NULL) {
+ free(data);
+ stb_vorbis_close(v);
+ return -2;
+ }
+ data = data2;
+ }
+ }
+ *output = data;
+ stb_vorbis_close(v);
+ return data_len;
+}
+#endif // NO_STDIO
+
+int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output)
+{
+ int data_len, offset, total, limit, error;
+ short *data;
+ stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL);
+ if (v == NULL) return -1;
+ limit = v->channels * 4096;
+ *channels = v->channels;
+ if (sample_rate)
+ *sample_rate = v->sample_rate;
+ offset = data_len = 0;
+ total = limit;
+ data = (short *) malloc(total * sizeof(*data));
+ if (data == NULL) {
+ stb_vorbis_close(v);
+ return -2;
+ }
+ for (;;) {
+ int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset);
+ if (n == 0) break;
+ data_len += n;
+ offset += n * v->channels;
+ if (offset + limit > total) {
+ short *data2;
+ total *= 2;
+ data2 = (short *) realloc(data, total * sizeof(*data));
+ if (data2 == NULL) {
+ free(data);
+ stb_vorbis_close(v);
+ return -2;
+ }
+ data = data2;
+ }
+ }
+ *output = data;
+ stb_vorbis_close(v);
+ return data_len;
+}
+#endif // STB_VORBIS_NO_INTEGER_CONVERSION
+
+int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats)
+{
+ float **outputs;
+ int len = num_floats / channels;
+ int n=0;
+ int z = f->channels;
+ if (z > channels) z = channels;
+ while (n < len) {
+ int i,j;
+ int k = f->channel_buffer_end - f->channel_buffer_start;
+ if (n+k >= len) k = len - n;
+ for (j=0; j < k; ++j) {
+ for (i=0; i < z; ++i)
+ *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j];
+ for ( ; i < channels; ++i)
+ *buffer++ = 0;
+ }
+ n += k;
+ f->channel_buffer_start += k;
+ if (n == len)
+ break;
+ if (!stb_vorbis_get_frame_float(f, NULL, &outputs))
+ break;
+ }
+ return n;
+}
+
+int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples)
+{
+ float **outputs;
+ int n=0;
+ int z = f->channels;
+ if (z > channels) z = channels;
+ while (n < num_samples) {
+ int i;
+ int k = f->channel_buffer_end - f->channel_buffer_start;
+ if (n+k >= num_samples) k = num_samples - n;
+ if (k) {
+ for (i=0; i < z; ++i)
+ memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k);
+ for ( ; i < channels; ++i)
+ memset(buffer[i]+n, 0, sizeof(float) * k);
+ }
+ n += k;
+ f->channel_buffer_start += k;
+ if (n == num_samples)
+ break;
+ if (!stb_vorbis_get_frame_float(f, NULL, &outputs))
+ break;
+ }
+ return n;
+}
+#endif // STB_VORBIS_NO_PULLDATA_API
+
+/* Version history
+ 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+ 1.11 - 2017-07-23 - fix MinGW compilation
+ 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+ 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version
+ 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks;
+ avoid discarding last frame of audio data
+ 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API
+ some more crash fixes when out of memory or with corrupt files
+ 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
+ some crash fixes when out of memory or with corrupt files
+ 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
+ 1.04 - 2014-08-27 - fix missing const-correct case in API
+ 1.03 - 2014-08-07 - Warning fixes
+ 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows
+ 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float
+ 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel
+ (API change) report sample rate for decode-full-file funcs
+ 0.99996 - bracket #include for macintosh compilation by Laurent Gomila
+ 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem
+ 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence
+ 0.99993 - remove assert that fired on legal files with empty tables
+ 0.99992 - rewind-to-start
+ 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo
+ 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++
+ 0.9998 - add a full-decode function with a memory source
+ 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition
+ 0.9996 - query length of vorbis stream in samples/seconds
+ 0.9995 - bugfix to another optimization that only happened in certain files
+ 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors
+ 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation
+ 0.9992 - performance improvement of IMDCT; now performs close to reference implementation
+ 0.9991 - performance improvement of IMDCT
+ 0.999 - (should have been 0.9990) performance improvement of IMDCT
+ 0.998 - no-CRT support from Casey Muratori
+ 0.997 - bugfixes for bugs found by Terje Mathisen
+ 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen
+ 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen
+ 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen
+ 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen
+ 0.992 - fixes for MinGW warning
+ 0.991 - turn fast-float-conversion on by default
+ 0.990 - fix push-mode seek recovery if you seek into the headers
+ 0.98b - fix to bad release of 0.98
+ 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode
+ 0.97 - builds under c++ (typecasting, don't use 'class' keyword)
+ 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code
+ 0.95 - clamping code for 16-bit functions
+ 0.94 - not publically released
+ 0.93 - fixed all-zero-floor case (was decoding garbage)
+ 0.92 - fixed a memory leak
+ 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION
+ 0.90 - first public release
+*/
+
+#endif // STB_VORBIS_HEADER_ONLY
+
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+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.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+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 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.
+------------------------------------------------------------------------------
+*/
+#include "soloud_file_hack_off.h"
\ No newline at end of file
diff --git a/src/soloud/src/audiosource/wav/stb_vorbis.h b/src/soloud/src/audiosource/wav/stb_vorbis.h
new file mode 100644
index 0000000..90a1570
--- /dev/null
+++ b/src/soloud/src/audiosource/wav/stb_vorbis.h
@@ -0,0 +1,2 @@
+#define STB_VORBIS_HEADER_ONLY
+#include "stb_vorbis.c"
\ No newline at end of file
diff --git a/src/soloud/src/backend/alsa/soloud_alsa.cpp b/src/soloud/src/backend/alsa/soloud_alsa.cpp
new file mode 100644
index 0000000..cc8609a
--- /dev/null
+++ b/src/soloud/src/backend/alsa/soloud_alsa.cpp
@@ -0,0 +1,168 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_thread.h"
+
+#if !defined(WITH_ALSA)
+
+namespace SoLoud
+{
+ result alsa_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+};
+
+#else
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#include
+
+#include
+#include
+#include
+
+namespace SoLoud
+{
+ struct ALSAData
+ {
+ float *buffer;
+ short *sampleBuffer;
+ snd_pcm_t *alsaDeviceHandle;
+ Soloud *soloud;
+ int samples;
+ int channels;
+ bool audioProcessingDone;
+ Thread::ThreadHandle threadHandle;
+ };
+
+
+ static void alsaThread(void *aParam)
+ {
+
+ ALSAData *data = static_cast(aParam);
+ while (!data->audioProcessingDone)
+ {
+ data->soloud->mix(data->buffer, data->samples);
+ for (int i=0;isamples*data->channels;++i)
+ {
+ data->sampleBuffer[i] = static_cast(floor(data->buffer[i]
+ * static_cast(0x7fff)));
+ }
+ if (snd_pcm_writei(data->alsaDeviceHandle, data->sampleBuffer, data->samples) == -EPIPE)
+ snd_pcm_prepare(data->alsaDeviceHandle);
+
+ }
+
+ }
+
+ static void alsaCleanup(Soloud *aSoloud)
+ {
+ if (0 == aSoloud->mBackendData)
+ {
+ return;
+ }
+ ALSAData *data = static_cast(aSoloud->mBackendData);
+ data->audioProcessingDone = true;
+ if (data->threadHandle)
+ {
+ Thread::wait(data->threadHandle);
+ Thread::release(data->threadHandle);
+ }
+ snd_pcm_drain(data->alsaDeviceHandle);
+ snd_pcm_close(data->alsaDeviceHandle);
+ if (0 != data->sampleBuffer)
+ {
+ delete[] data->sampleBuffer;
+ }
+ if (0 != data->buffer)
+ {
+ delete[] data->buffer;
+ }
+ delete data;
+ aSoloud->mBackendData = 0;
+ }
+
+ result alsa_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ ALSAData *data = new ALSAData;
+ memset(data, 0, sizeof(ALSAData));
+ aSoloud->mBackendData = data;
+ aSoloud->mBackendCleanupFunc = alsaCleanup;
+ data->samples = aBuffer;
+ data->channels = 2;
+ data->soloud = aSoloud;
+
+ int rc;
+ snd_pcm_t *handle;
+ rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
+ if (rc < 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+
+ data->alsaDeviceHandle = handle;
+
+ snd_pcm_hw_params_t *params;
+ snd_pcm_hw_params_alloca(¶ms);
+ snd_pcm_hw_params_any(handle, params);
+
+ snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
+ snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
+ snd_pcm_hw_params_set_channels(handle, params, 2);
+ snd_pcm_hw_params_set_buffer_size(handle, params, aBuffer);
+
+ unsigned int val = aSamplerate;
+ int dir = 0;
+ rc = snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
+ if (rc < 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+
+ rc = snd_pcm_hw_params(handle, params);
+ if (rc < 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+
+ snd_pcm_hw_params_get_rate(params, &val, &dir);
+ aSamplerate = val;
+ snd_pcm_hw_params_get_channels(params, &val);
+ data->channels = val;
+
+ data->buffer = new float[data->samples*data->channels];
+ data->sampleBuffer = new short[data->samples*data->channels];
+ aSoloud->postinit(aSamplerate, data->samples * data->channels, aFlags, 2);
+ data->threadHandle = Thread::createThread(alsaThread, data);
+ if (0 == data->threadHandle)
+ {
+ return UNKNOWN_ERROR;
+ }
+ aSoloud->mBackendString = "ALSA";
+ return 0;
+ }
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/coreaudio/soloud_coreaudio.cpp b/src/soloud/src/backend/coreaudio/soloud_coreaudio.cpp
new file mode 100644
index 0000000..158a4d7
--- /dev/null
+++ b/src/soloud/src/backend/coreaudio/soloud_coreaudio.cpp
@@ -0,0 +1,127 @@
+/*
+SoLoud audio engine
+Copyright (c) 2015 Jari Komppa
+
+Core Audio backend for Mac OS X
+Copyright (c) 2015 Petri Häkkinen
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+
+#include "soloud.h"
+#include "soloud_thread.h"
+
+#if !defined(WITH_COREAUDIO)
+
+namespace SoLoud
+{
+ result coreaudio_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+}
+#else
+
+#include
+
+#define NUM_BUFFERS 2
+
+static AudioQueueRef audioQueue = 0;
+
+namespace SoLoud
+{
+ void soloud_coreaudio_deinit(SoLoud::Soloud *aSoloud)
+ {
+ AudioQueueStop(audioQueue, true);
+ AudioQueueDispose(audioQueue, false);
+ }
+
+ static void coreaudio_mutex_lock(void *mutex)
+ {
+ Thread::lockMutex(mutex);
+ }
+
+ static void coreaudio_mutex_unlock(void *mutex)
+ {
+ Thread::unlockMutex(mutex);
+ }
+
+ static void coreaudio_fill_buffer(void *context, AudioQueueRef queue, AudioQueueBufferRef buffer)
+ {
+ SoLoud::Soloud *aSoloud = (SoLoud::Soloud*)context;
+ aSoloud->mixSigned16((short*)buffer->mAudioData, buffer->mAudioDataByteSize / 4);
+ AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
+ }
+
+ result coreaudio_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ aSoloud->postinit(aSamplerate, aBuffer, aFlags, 2);
+ aSoloud->mBackendCleanupFunc = soloud_coreaudio_deinit;
+
+ AudioStreamBasicDescription audioFormat;
+ audioFormat.mSampleRate = aSamplerate;
+ audioFormat.mFormatID = kAudioFormatLinearPCM;
+ audioFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
+ audioFormat.mBytesPerPacket = 4;
+ audioFormat.mFramesPerPacket = 1;
+ audioFormat.mBytesPerFrame = 4;
+ audioFormat.mChannelsPerFrame = 2;
+ audioFormat.mBitsPerChannel = 16;
+ audioFormat.mReserved = 0;
+
+ // create the audio queue
+ OSStatus result = AudioQueueNewOutput(&audioFormat, coreaudio_fill_buffer, aSoloud, NULL, NULL, 0, &audioQueue);
+ if(result)
+ {
+ //printf("AudioQueueNewOutput failed (%d)\n", result);
+ return UNKNOWN_ERROR;
+ }
+
+ // allocate and prime audio buffers
+ for(int i = 0; i < NUM_BUFFERS; ++i)
+ {
+ AudioQueueBufferRef buffer;
+ result = AudioQueueAllocateBuffer(audioQueue, aBuffer * 4, &buffer);
+ if(result)
+ {
+ //printf("AudioQueueAllocateBuffer failed (%d)\n", result);
+ return UNKNOWN_ERROR;
+ }
+ buffer->mAudioDataByteSize = aBuffer * 4;
+ memset(buffer->mAudioData, 0, buffer->mAudioDataByteSize);
+ AudioQueueEnqueueBuffer(audioQueue, buffer, 0, NULL);
+ }
+
+ // start playback
+ result = AudioQueueStart(audioQueue, NULL);
+ if(result)
+ {
+ //printf("AudioQueueStart failed (%d)\n", result);
+ return UNKNOWN_ERROR;
+ }
+
+ aSoloud->mBackendString = "CoreAudio";
+ return 0;
+ }
+};
+#endif
diff --git a/src/soloud/src/backend/null/soloud_null.cpp b/src/soloud/src/backend/null/soloud_null.cpp
new file mode 100644
index 0000000..330d077
--- /dev/null
+++ b/src/soloud/src/backend/null/soloud_null.cpp
@@ -0,0 +1,57 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+
+#if !defined(WITH_NULL)
+
+namespace SoLoud
+{
+ result null_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+};
+
+#else
+
+namespace SoLoud
+{
+ static void nullCleanup(Soloud *aSoloud)
+ {
+ }
+
+ result null_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ if (aChannels == 0 || aChannels == 3 || aChannels == 5 || aChannels == 7 || aChannels > MAX_CHANNELS || aBuffer < SAMPLE_GRANULARITY)
+ return INVALID_PARAMETER;
+ aSoloud->mBackendData = 0;
+ aSoloud->mBackendCleanupFunc = nullCleanup;
+
+ aSoloud->postinit(aSamplerate, aBuffer, aFlags, aChannels);
+ aSoloud->mBackendString = "null driver";
+ return SO_NO_ERROR;
+ }
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/opensles/soloud_opensles.cpp b/src/soloud/src/backend/opensles/soloud_opensles.cpp
new file mode 100644
index 0000000..e3bf9fe
--- /dev/null
+++ b/src/soloud/src/backend/opensles/soloud_opensles.cpp
@@ -0,0 +1,301 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+#include
+#include
+
+#include "soloud.h"
+#include "soloud_thread.h"
+
+#if !defined(WITH_OPENSLES)
+namespace SoLoud
+{
+ result opensles_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+}
+#else
+
+#include "SLES/OpenSLES.h"
+#include "SLES/OpenSLES_Platform.h"
+
+#if defined( __ANDROID__ )
+#include "SLES/OpenSLES_Android.h"
+#endif
+
+// Error logging.
+#if defined( __ANDROID__ )
+# include
+# define LOG_ERROR( _msg ) \
+ __android_log_print( ANDROID_LOG_ERROR, "SoLoud", _msg )
+# define LOG_INFO( _msg ) \
+ __android_log_print( ANDROID_LOG_INFO, "SoLoud", _msg )
+
+#else
+ printf( _msg )
+#endif
+
+#define NUM_BUFFERS 2
+
+namespace SoLoud
+{
+ struct BackendData
+ {
+ BackendData()
+ {
+ memset(this, 0, sizeof(BackendData));
+ }
+
+ ~BackendData()
+ {
+ // Wait until thread is done.
+ threadrun++;
+ while (threadrun == 1)
+ {
+ Thread::sleep(10);
+ }
+
+ if(playerObj)
+ {
+ (*playerObj)->Destroy(playerObj);
+ }
+
+ if(outputMixObj)
+ {
+ (*outputMixObj)->Destroy(outputMixObj);
+ }
+
+ if(engineObj)
+ {
+ (*engineObj)->Destroy(engineObj);
+ }
+
+ for(int idx = 0; idx < NUM_BUFFERS; ++idx)
+ {
+ delete [] outputBuffers[idx];
+ }
+ }
+
+ // Engine.
+ SLObjectItf engineObj;
+ SLEngineItf engine;
+
+ // Output mix.
+ SLObjectItf outputMixObj;
+ SLVolumeItf outputMixVol;
+
+ // Data.
+ SLDataLocator_OutputMix outLocator;
+ SLDataSink dstDataSink;
+
+ // Player.
+ SLObjectItf playerObj;
+ SLPlayItf player;
+ SLVolumeItf playerVol;
+ SLAndroidSimpleBufferQueueItf playerBufferQueue;
+
+ unsigned int bufferSize;
+ unsigned int channels;
+ short *outputBuffers[ NUM_BUFFERS ];
+ int buffersQueued;
+ int activeBuffer;
+ volatile int threadrun;
+
+ SLDataLocator_AndroidSimpleBufferQueue inLocator;
+ };
+
+ void soloud_opensles_deinit(SoLoud::Soloud *aSoloud)
+ {
+ BackendData *data = static_cast(aSoloud->mBackendData);
+ delete data;
+ aSoloud->mBackendData = NULL;
+ }
+
+ static void opensles_iterate(SoLoud::Soloud *aSoloud)
+ {
+ BackendData *data = static_cast(aSoloud->mBackendData);
+
+ // If we have no buffered queued, queue one up for playback.
+ if(data->buffersQueued == 0)
+ {
+ // Get next output buffer, advance, next buffer.
+ short * outputBuffer = data->outputBuffers[data->activeBuffer];
+ data->activeBuffer = (data->activeBuffer + 1) % NUM_BUFFERS;
+ short * nextBuffer = data->outputBuffers[data->activeBuffer];
+
+ // Mix this buffer.
+ const int bufferSizeBytes = data->bufferSize * data->channels * sizeof(short);
+ (*data->playerBufferQueue)->Enqueue(data->playerBufferQueue, outputBuffer, bufferSizeBytes);
+ ++data->buffersQueued;
+
+ aSoloud->mixSigned16(nextBuffer,data->bufferSize);
+ }
+ }
+
+ static void opensles_thread(void *aParam)
+ {
+ Soloud *soloud = static_cast(aParam);
+ BackendData *data = static_cast(soloud->mBackendData);
+ while (data->threadrun == 0)
+ {
+ opensles_iterate(soloud);
+
+ // TODO: Condition var?
+ Thread::sleep(1);
+ }
+ data->threadrun++;
+ }
+
+ static void SLAPIENTRY soloud_opensles_play_callback(SLPlayItf player, void *context, SLuint32 event)
+ {
+ Soloud *soloud = static_cast(context);
+ BackendData *data = static_cast(soloud->mBackendData);
+ if( event & SL_PLAYEVENT_HEADATEND )
+ {
+ data->buffersQueued--;
+ }
+ }
+
+ result opensles_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ BackendData *data = new BackendData();
+
+ // Allocate output buffer to mix into.
+ data->bufferSize = aBuffer;
+ data->channels = aChannels;
+ const int bufferSizeBytes = data->bufferSize * data->channels * sizeof(short);
+ for(int idx = 0; idx < NUM_BUFFERS; ++idx)
+ {
+ data->outputBuffers[idx] = new short[ data->bufferSize * data->channels ];
+ memset(data->outputBuffers[idx], 0, bufferSizeBytes);
+ }
+
+ // Create engine.
+ const SLEngineOption opts[] = { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE };
+ if(slCreateEngine( &data->engineObj, 1, opts, 0, NULL, NULL ) != SL_RESULT_SUCCESS)
+ {
+ LOG_ERROR( "Failed to create engine." );
+ return UNKNOWN_ERROR;
+ }
+
+ // Realize and get engine interfaxce.
+ (*data->engineObj)->Realize(data->engineObj, SL_BOOLEAN_FALSE);
+ if((*data->engineObj)->GetInterface(data->engineObj, SL_IID_ENGINE, &data->engine) != SL_RESULT_SUCCESS)
+ {
+ LOG_ERROR( "Failed to get engine interface." );
+ return UNKNOWN_ERROR;
+ }
+
+ // Create output mix.
+ const SLInterfaceID ids[] = { SL_IID_VOLUME };
+ const SLboolean req[] = { SL_BOOLEAN_FALSE };
+
+ if((*data->engine)->CreateOutputMix(data->engine, &data->outputMixObj, 1, ids, req) != SL_RESULT_SUCCESS)
+ {
+ LOG_ERROR( "Failed to create output mix object." );
+ return UNKNOWN_ERROR;
+ }
+ (*data->outputMixObj)->Realize(data->outputMixObj, SL_BOOLEAN_FALSE);
+
+ if((*data->outputMixObj)->GetInterface(data->outputMixObj, SL_IID_VOLUME, &data->outputMixVol) != SL_RESULT_SUCCESS)
+ {
+ LOG_INFO( "Failed to get output mix volume interface." );
+ }
+
+ // Create android buffer queue.
+ data->inLocator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
+ data->inLocator.numBuffers = NUM_BUFFERS;
+
+ // Setup data format.
+ SLDataFormat_PCM format;
+ format.formatType = SL_DATAFORMAT_PCM;
+ format.numChannels = data->channels;
+ format.samplesPerSec = aSamplerate * 1000; //mHz
+ format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
+ format.containerSize = 16;
+ format.endianness = SL_BYTEORDER_LITTLEENDIAN;
+
+ // Determine channel mask.
+ if(data->channels == 2)
+ {
+ format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+ }
+ else
+ {
+ format.channelMask = SL_SPEAKER_FRONT_CENTER;
+ }
+
+ SLDataSource src;
+ src.pLocator = &data->inLocator;
+ src.pFormat = &format;
+
+ // Output mix.
+ data->outLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
+ data->outLocator.outputMix = data->outputMixObj;
+
+ data->dstDataSink.pLocator = &data->outLocator;
+ data->dstDataSink.pFormat = NULL;
+
+ // Setup player.
+ {
+ const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
+ const SLboolean req[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
+
+ (*data->engine)->CreateAudioPlayer(data->engine, &data->playerObj, &src, &data->dstDataSink, sizeof(ids) / sizeof(ids[0]), ids, req);
+
+ (*data->playerObj)->Realize(data->playerObj, SL_BOOLEAN_FALSE);
+
+ (*data->playerObj)->GetInterface(data->playerObj, SL_IID_PLAY, &data->player);
+ (*data->playerObj)->GetInterface(data->playerObj, SL_IID_VOLUME, &data->playerVol);
+
+ (*data->playerObj)->GetInterface(data->playerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &data->playerBufferQueue);
+ }
+
+ aSoloud->mBackendData = data; // Must be set before callback
+
+ // Begin playing.
+ {
+ const int bufferSizeBytes = data->bufferSize * data->channels * sizeof(short);
+ (*data->playerBufferQueue)->Enqueue(data->playerBufferQueue, data->outputBuffers[0], bufferSizeBytes);
+ data->activeBuffer = (data->activeBuffer + 1) % NUM_BUFFERS;
+
+ (*data->player)->RegisterCallback(data->player, soloud_opensles_play_callback, aSoloud);
+ (*data->player)->SetCallbackEventsMask(data->player, SL_PLAYEVENT_HEADATEND);
+ (*data->player)->SetPlayState(data->player, SL_PLAYSTATE_PLAYING);
+
+ }
+
+ //
+ aSoloud->postinit(aSamplerate,data->bufferSize,aFlags,2);
+ aSoloud->mBackendCleanupFunc = soloud_opensles_deinit;
+
+ LOG_INFO( "Creating audio thread." );
+ Thread::createThread(opensles_thread, (void*)aSoloud);
+
+ aSoloud->mBackendString = "OpenSL ES";
+ return SO_NO_ERROR;
+ }
+};
+#endif
diff --git a/src/soloud/src/backend/oss/soloud_oss.cpp b/src/soloud/src/backend/oss/soloud_oss.cpp
new file mode 100644
index 0000000..66495f5
--- /dev/null
+++ b/src/soloud/src/backend/oss/soloud_oss.cpp
@@ -0,0 +1,179 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_thread.h"
+
+#if !defined(WITH_OSS)
+
+namespace SoLoud
+{
+ result oss_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+};
+
+#else
+#include
+#include
+#include
+#include
+#include
+
+namespace SoLoud
+{
+ static const int OSS_DEVICE_COUNT = 4;
+ static const char *OSS_DEVICES[OSS_DEVICE_COUNT] =
+ {
+ "/dev/dsp",
+ "/dev/dsp0.0",
+ "/dev/dsp1.0",
+ "/dev/dsp2.0"
+ };
+
+ struct OSSData
+ {
+ float *buffer;
+ short *sampleBuffer;
+ int ossDeviceHandle;
+ Soloud *soloud;
+ int samples;
+ int channels;
+ bool audioProcessingDone;
+ Thread::ThreadHandle threadHandle;
+ };
+
+ static void ossThread(void *aParam)
+ {
+ OSSData *data = static_cast(aParam);
+ while (!data->audioProcessingDone)
+ {
+ data->soloud->mix(data->buffer, data->samples);
+ for (int i=0;isamples*data->channels;++i)
+ {
+ data->sampleBuffer[i] = static_cast(floor(data->buffer[i]
+ * static_cast(0x7fff)));
+ }
+ write(data->ossDeviceHandle, data->sampleBuffer,
+ sizeof(short)*data->samples*data->channels);
+ }
+ }
+
+ static void ossCleanup(Soloud *aSoloud)
+ {
+ if (0 == aSoloud->mBackendData)
+ {
+ return;
+ }
+ OSSData *data = static_cast(aSoloud->mBackendData);
+ data->audioProcessingDone = true;
+ if (data->threadHandle)
+ {
+ Thread::wait(data->threadHandle);
+ Thread::release(data->threadHandle);
+ }
+ ioctl(data->ossDeviceHandle, SNDCTL_DSP_RESET, 0);
+ if (0 != data->sampleBuffer)
+ {
+ delete[] data->sampleBuffer;
+ }
+ if (0 != data->buffer)
+ {
+ delete[] data->buffer;
+ }
+ close(data->ossDeviceHandle);
+ delete data;
+ aSoloud->mBackendData = 0;
+ }
+
+ result oss_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ OSSData *data = new OSSData;
+ memset(data, 0, sizeof(OSSData));
+ aSoloud->mBackendData = data;
+ aSoloud->mBackendCleanupFunc = ossCleanup;
+ data->samples = aBuffer;
+ data->soloud = aSoloud;
+ bool deviceIsOpen = false;
+ for (int i=0;iossDeviceHandle = open(OSS_DEVICES[i], O_WRONLY, 0);
+ if (-1 != data->ossDeviceHandle)
+ {
+ deviceIsOpen = true;
+ break;
+ }
+ }
+ if (!deviceIsOpen)
+ {
+ return UNKNOWN_ERROR;
+ }
+ int flags = fcntl(data->ossDeviceHandle, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ if (-1 == fcntl(data->ossDeviceHandle, F_SETFL, flags))
+ {
+ return UNKNOWN_ERROR;
+ }
+ int format = AFMT_S16_NE;
+ if (-1 == ioctl(data->ossDeviceHandle, SNDCTL_DSP_SETFMT, &format))
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (format != AFMT_S16_NE)
+ {
+ return UNKNOWN_ERROR;
+ }
+ int channels = 2;
+ data->channels = channels;
+ if (-1 == ioctl(data->ossDeviceHandle, SNDCTL_DSP_CHANNELS, &data->channels))
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (channels != data->channels)
+ {
+ return UNKNOWN_ERROR;
+ }
+ int speed = aSamplerate;
+ if (-1 == ioctl(data->ossDeviceHandle, SNDCTL_DSP_SPEED, &speed))
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (speed != aSamplerate)
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->buffer = new float[data->samples*data->channels];
+ data->sampleBuffer = new short[data->samples*data->channels];
+ aSoloud->postinit(aSamplerate, data->samples * data->channels, aFlags, 2);
+ data->threadHandle = Thread::createThread(ossThread, data);
+ if (0 == data->threadHandle)
+ {
+ return UNKNOWN_ERROR;
+ }
+ aSoloud->mBackendString = "OSS";
+ return 0;
+ }
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/sdl/soloud_sdl1.cpp b/src/soloud/src/backend/sdl/soloud_sdl1.cpp
new file mode 100644
index 0000000..1d74a74
--- /dev/null
+++ b/src/soloud/src/backend/sdl/soloud_sdl1.cpp
@@ -0,0 +1,102 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+
+#include "soloud.h"
+
+#if !defined(WITH_SDL1)
+
+namespace SoLoud
+{
+ result sdl1_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+}
+
+#else
+
+#if defined(_MSC_VER)
+#include
+#else
+#include
+#endif
+#include
+
+
+extern "C"
+{
+ int dll_SDL1_found();
+ int dll_SDL1_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained);
+ void dll_SDL1_CloseAudio();
+ void dll_SDL1_PauseAudio(int pause_on);
+};
+
+
+namespace SoLoud
+{
+ static SDL_AudioSpec gActiveAudioSpec;
+
+ void soloud_sdl1_audiomixer(void *userdata, Uint8 *stream, int len)
+ {
+ short *buf = (short*)stream;
+ SoLoud::Soloud *soloud = (SoLoud::Soloud *)userdata;
+ int samples = len / (gActiveAudioSpec.channels * sizeof(short));
+ soloud->mixSigned16(buf, samples);
+ }
+
+ static void soloud_sdl1_deinit(SoLoud::Soloud *aSoloud)
+ {
+ dll_SDL1_CloseAudio();
+ }
+
+ result sdl1_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ if (!dll_SDL1_found())
+ return DLL_NOT_FOUND;
+
+ SDL_AudioSpec as;
+ as.freq = aSamplerate;
+ as.format = AUDIO_S16;
+ as.channels = aChannels;
+ as.samples = aBuffer;
+ as.callback = soloud_sdl1_audiomixer;
+ as.userdata = (void*)aSoloud;
+
+ if (dll_SDL1_OpenAudio(&as, &gActiveAudioSpec) < 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+
+ aSoloud->postinit(gActiveAudioSpec.freq, gActiveAudioSpec.samples, aFlags, gActiveAudioSpec.channels);
+
+ aSoloud->mBackendCleanupFunc = soloud_sdl1_deinit;
+
+ dll_SDL1_PauseAudio(0);
+ aSoloud->mBackendString = "SDL (dynamic)";
+ return 0;
+ }
+
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/sdl/soloud_sdl1_dll.c b/src/soloud/src/backend/sdl/soloud_sdl1_dll.c
new file mode 100644
index 0000000..7f0d2cb
--- /dev/null
+++ b/src/soloud/src/backend/sdl/soloud_sdl1_dll.c
@@ -0,0 +1,128 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+#if defined(_MSC_VER)
+#define WINDOWS_VERSION
+#include
+#else
+#include
+#endif
+#include
+
+typedef int (*SDLOpenAudio)(SDL_AudioSpec *desired, SDL_AudioSpec *obtained);
+typedef void (*SDLCloseAudio)();
+typedef void (*SDLPauseAudio)(int pause_on);
+
+static SDLOpenAudio dSDL1_OpenAudio = NULL;
+static SDLCloseAudio dSDL1_CloseAudio = NULL;
+static SDLPauseAudio dSDL1_PauseAudio = NULL;
+
+#ifdef WINDOWS_VERSION
+#include
+
+static HMODULE openDll()
+{
+ HMODULE res = LoadLibraryA("SDL.dll");
+ return res;
+}
+
+static void* getDllProc(HMODULE aDllHandle, const char *aProcName)
+{
+ return GetProcAddress(aDllHandle, aProcName);
+}
+
+#else
+#include // dll functions
+
+static void * openDll()
+{
+ void * res;
+ res = dlopen("/Library/Frameworks/SDL.framework/SDL", RTLD_LAZY);
+ if (!res) res = dlopen("SDL.so", RTLD_LAZY);
+ return res;
+}
+
+static void* getDllProc(void * aLibrary, const char *aProcName)
+{
+ return dlsym(aLibrary, aProcName);
+}
+
+#endif
+
+static int load_dll()
+{
+#ifdef WINDOWS_VERSION
+ HMODULE dll = NULL;
+#else
+ void * dll = NULL;
+#endif
+
+ if (dSDL1_OpenAudio != NULL)
+ {
+ return 1;
+ }
+
+ dll = openDll();
+
+ if (dll)
+ {
+ dSDL1_OpenAudio = (SDLOpenAudio)getDllProc(dll, "SDL_OpenAudio");
+ dSDL1_CloseAudio = (SDLCloseAudio)getDllProc(dll, "SDL_CloseAudio");
+ dSDL1_PauseAudio = (SDLPauseAudio)getDllProc(dll, "SDL_PauseAudio");
+
+
+ if (dSDL1_OpenAudio &&
+ dSDL1_CloseAudio &&
+ dSDL1_PauseAudio)
+ {
+ return 1;
+ }
+ }
+ dSDL1_OpenAudio = NULL;
+ return 0;
+}
+
+int dll_SDL1_found()
+{
+ return load_dll();
+}
+
+int dll_SDL1_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
+{
+ if (load_dll())
+ return dSDL1_OpenAudio(desired, obtained);
+ return 0;
+}
+
+void dll_SDL1_CloseAudio()
+{
+ if (load_dll())
+ dSDL1_CloseAudio();
+}
+
+void dll_SDL1_PauseAudio(int pause_on)
+{
+ if (load_dll())
+ dSDL1_PauseAudio(pause_on);
+}
diff --git a/src/soloud/src/backend/sdl/soloud_sdl2.cpp b/src/soloud/src/backend/sdl/soloud_sdl2.cpp
new file mode 100644
index 0000000..1ff1414
--- /dev/null
+++ b/src/soloud/src/backend/sdl/soloud_sdl2.cpp
@@ -0,0 +1,133 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+
+#include "soloud.h"
+
+#if !defined(WITH_SDL2)
+
+namespace SoLoud
+{
+ result sdl2_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+}
+
+#else
+
+#if defined(_MSC_VER)
+#include
+#else
+#include
+#endif
+#include
+
+
+extern "C"
+{
+ int dll_SDL2_found();
+
+ Uint32 dll_SDL2_WasInit(Uint32 flags);
+ int dll_SDL2_InitSubSystem(Uint32 flags);
+ SDL_AudioDeviceID dll_SDL2_OpenAudioDevice(const char* device,
+ int iscapture,
+ const SDL_AudioSpec* desired,
+ SDL_AudioSpec* obtained,
+ int allowed_changes);
+ void dll_SDL2_CloseAudioDevice(SDL_AudioDeviceID dev);
+ void dll_SDL2_PauseAudioDevice(SDL_AudioDeviceID dev,
+ int pause_on);
+};
+
+
+namespace SoLoud
+{
+ static SDL_AudioSpec gActiveAudioSpec;
+ static SDL_AudioDeviceID gAudioDeviceID;
+
+ void soloud_sdl2_audiomixer(void *userdata, Uint8 *stream, int len)
+ {
+ short *buf = (short*)stream;
+ SoLoud::Soloud *soloud = (SoLoud::Soloud *)userdata;
+ if (gActiveAudioSpec.format == AUDIO_F32)
+ {
+ int samples = len / (gActiveAudioSpec.channels * sizeof(float));
+ soloud->mix((float *)buf, samples);
+ }
+ else // assume s16 if not float
+ {
+ int samples = len / (gActiveAudioSpec.channels * sizeof(short));
+ soloud->mixSigned16(buf, samples);
+ }
+ }
+
+ static void soloud_sdl2_deinit(SoLoud::Soloud *aSoloud)
+ {
+ dll_SDL2_CloseAudioDevice(gAudioDeviceID);
+ }
+
+ result sdl2_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ if (!dll_SDL2_found())
+ return DLL_NOT_FOUND;
+
+ if (!dll_SDL2_WasInit(SDL_INIT_AUDIO))
+ {
+ if (dll_SDL2_InitSubSystem(SDL_INIT_AUDIO) < 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ SDL_AudioSpec as;
+ as.freq = aSamplerate;
+ as.format = AUDIO_F32;
+ as.channels = aChannels;
+ as.samples = aBuffer;
+ as.callback = soloud_sdl2_audiomixer;
+ as.userdata = (void*)aSoloud;
+
+ gAudioDeviceID = dll_SDL2_OpenAudioDevice(NULL, 0, &as, &gActiveAudioSpec, SDL_AUDIO_ALLOW_ANY_CHANGE & ~(SDL_AUDIO_ALLOW_FORMAT_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE));
+ if (gAudioDeviceID == 0)
+ {
+ as.format = AUDIO_S16;
+ gAudioDeviceID = dll_SDL2_OpenAudioDevice(NULL, 0, &as, &gActiveAudioSpec, SDL_AUDIO_ALLOW_ANY_CHANGE & ~(SDL_AUDIO_ALLOW_FORMAT_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE));
+ if (gAudioDeviceID == 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ aSoloud->postinit(gActiveAudioSpec.freq, gActiveAudioSpec.samples, aFlags, gActiveAudioSpec.channels);
+
+ aSoloud->mBackendCleanupFunc = soloud_sdl2_deinit;
+
+ dll_SDL2_PauseAudioDevice(gAudioDeviceID, 0);
+ aSoloud->mBackendString = "SDL2 (dynamic)";
+ return 0;
+ }
+
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/sdl/soloud_sdl2_dll.c b/src/soloud/src/backend/sdl/soloud_sdl2_dll.c
new file mode 100644
index 0000000..d67ba66
--- /dev/null
+++ b/src/soloud/src/backend/sdl/soloud_sdl2_dll.c
@@ -0,0 +1,160 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+#if defined(_MSC_VER)
+#define WINDOWS_VERSION
+#include
+#else
+#include
+#endif
+#include
+
+
+typedef Uint32 (*SDL2_WasInit_t)(Uint32 flags);
+typedef int (*SDL2_InitSubSystem_t)(Uint32 flags);
+typedef SDL_AudioDeviceID (*SDL2_OpenAudioDevice_t)(const char* device,
+ int iscapture,
+ const SDL_AudioSpec* desired,
+ SDL_AudioSpec* obtained,
+ int allowed_changes);
+typedef void (*SDL2_CloseAudioDevice_t)(SDL_AudioDeviceID dev);
+typedef void (*SDL2_PauseAudioDevice_t)(SDL_AudioDeviceID dev,
+ int pause_on);
+
+static SDL2_WasInit_t SDL2_WasInit = NULL;
+static SDL2_InitSubSystem_t SDL2_InitSubSystem = NULL;
+static SDL2_OpenAudioDevice_t SDL2_OpenAudioDevice = NULL;
+static SDL2_CloseAudioDevice_t SDL2_CloseAudioDevice = NULL;
+static SDL2_PauseAudioDevice_t SDL2_PauseAudioDevice = NULL;
+
+#ifdef WINDOWS_VERSION
+#include
+
+static HMODULE openDll()
+{
+ HMODULE res = LoadLibraryA("SDL2.dll");
+ return res;
+}
+
+static void* getDllProc(HMODULE aDllHandle, const char *aProcName)
+{
+ return GetProcAddress(aDllHandle, aProcName);
+}
+
+#else
+#include // dll functions
+
+static void * openDll()
+{
+ void * res;
+ res = dlopen("/Library/Frameworks/SDL2.framework/SDL2", RTLD_LAZY);
+ if (!res) res = dlopen("SDL2.so", RTLD_LAZY);
+ return res;
+}
+
+static void* getDllProc(void * aLibrary, const char *aProcName)
+{
+ return dlsym(aLibrary, aProcName);
+}
+
+#endif
+
+static int load_dll()
+{
+#ifdef WINDOWS_VERSION
+ HMODULE dll = NULL;
+#else
+ void * dll = NULL;
+#endif
+
+ if (SDL2_OpenAudioDevice != NULL)
+ {
+ return 1;
+ }
+
+ dll = openDll();
+
+ if (dll)
+ {
+ SDL2_WasInit = (SDL2_WasInit_t)getDllProc(dll, "SDL_WasInit");
+ SDL2_InitSubSystem = (SDL2_InitSubSystem_t)getDllProc(dll, "SDL_InitSubSystem");
+ SDL2_OpenAudioDevice = (SDL2_OpenAudioDevice_t)getDllProc(dll, "SDL_OpenAudioDevice");
+ SDL2_CloseAudioDevice = (SDL2_CloseAudioDevice_t)getDllProc(dll, "SDL_CloseAudioDevice");
+ SDL2_PauseAudioDevice = (SDL2_PauseAudioDevice_t)getDllProc(dll, "SDL_PauseAudioDevice");
+
+ if (SDL2_WasInit &&
+ SDL2_InitSubSystem &&
+ SDL2_OpenAudioDevice &&
+ SDL2_CloseAudioDevice &&
+ SDL2_PauseAudioDevice)
+ {
+ return 1;
+ }
+ }
+ SDL2_OpenAudioDevice = NULL;
+ return 0;
+}
+
+int dll_SDL2_found()
+{
+ return load_dll();
+}
+
+Uint32 dll_SDL2_WasInit(Uint32 flags)
+{
+ if (SDL2_WasInit)
+ return SDL2_WasInit(flags);
+ return 0;
+}
+
+int dll_SDL2_InitSubSystem(Uint32 flags)
+{
+ if (SDL2_InitSubSystem)
+ return SDL2_InitSubSystem(flags);
+ return -1;
+}
+
+SDL_AudioDeviceID dll_SDL2_OpenAudioDevice(const char* device,
+ int iscapture,
+ const SDL_AudioSpec* desired,
+ SDL_AudioSpec* obtained,
+ int allowed_changes)
+{
+ if (SDL2_OpenAudioDevice)
+ return SDL2_OpenAudioDevice(device, iscapture, desired, obtained, allowed_changes);
+ return 0;
+}
+
+void dll_SDL2_CloseAudioDevice(SDL_AudioDeviceID dev)
+{
+ if (SDL2_CloseAudioDevice)
+ SDL2_CloseAudioDevice(dev);
+}
+
+void dll_SDL2_PauseAudioDevice(SDL_AudioDeviceID dev,
+ int pause_on)
+{
+ if (SDL2_PauseAudioDevice)
+ SDL2_PauseAudioDevice(dev, pause_on);
+}
diff --git a/src/soloud/src/backend/sdl2_static/soloud_sdl2_static.cpp b/src/soloud/src/backend/sdl2_static/soloud_sdl2_static.cpp
new file mode 100644
index 0000000..25744f3
--- /dev/null
+++ b/src/soloud/src/backend/sdl2_static/soloud_sdl2_static.cpp
@@ -0,0 +1,111 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+
+#include "soloud.h"
+
+#if !defined(WITH_SDL2_STATIC)
+
+namespace SoLoud
+{
+ result sdl2static_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+}
+
+#else
+
+#if defined(_MSC_VER)
+#include
+#else
+#include
+#endif
+#include
+
+namespace SoLoud
+{
+ static SDL_AudioSpec gActiveAudioSpec;
+ static SDL_AudioDeviceID gAudioDeviceID;
+
+ void soloud_sdl2static_audiomixer(void *userdata, Uint8 *stream, int len)
+ {
+ short *buf = (short*)stream;
+ SoLoud::Soloud *soloud = (SoLoud::Soloud *)userdata;
+ if (gActiveAudioSpec.format == AUDIO_F32)
+ {
+ int samples = len / (gActiveAudioSpec.channels * sizeof(float));
+ soloud->mix((float *)buf, samples);
+ }
+ else // assume s16 if not float
+ {
+ int samples = len / (gActiveAudioSpec.channels * sizeof(short));
+ soloud->mixSigned16(buf, samples);
+ }
+ }
+
+ static void soloud_sdl2static_deinit(SoLoud::Soloud *aSoloud)
+ {
+ SDL_CloseAudioDevice(gAudioDeviceID);
+ }
+
+ result sdl2static_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ if (!SDL_WasInit(SDL_INIT_AUDIO))
+ {
+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ SDL_AudioSpec as;
+ as.freq = aSamplerate;
+ as.format = AUDIO_F32;
+ as.channels = aChannels;
+ as.samples = aBuffer;
+ as.callback = soloud_sdl2static_audiomixer;
+ as.userdata = (void*)aSoloud;
+
+ gAudioDeviceID = SDL_OpenAudioDevice(NULL, 0, &as, &gActiveAudioSpec, SDL_AUDIO_ALLOW_ANY_CHANGE & ~(SDL_AUDIO_ALLOW_FORMAT_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE));
+ if (gAudioDeviceID == 0)
+ {
+ as.format = AUDIO_S16;
+ gAudioDeviceID = SDL_OpenAudioDevice(NULL, 0, &as, &gActiveAudioSpec, SDL_AUDIO_ALLOW_ANY_CHANGE & ~(SDL_AUDIO_ALLOW_FORMAT_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE));
+ if (gAudioDeviceID == 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ aSoloud->postinit(gActiveAudioSpec.freq, gActiveAudioSpec.samples, aFlags, gActiveAudioSpec.channels);
+
+ aSoloud->mBackendCleanupFunc = soloud_sdl2static_deinit;
+
+ SDL_PauseAudioDevice(gAudioDeviceID, 0);
+ aSoloud->mBackendString = "SDL2 (static)";
+ return 0;
+ }
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/sdl_static/soloud_sdl_static.cpp b/src/soloud/src/backend/sdl_static/soloud_sdl_static.cpp
new file mode 100644
index 0000000..f7d9408
--- /dev/null
+++ b/src/soloud/src/backend/sdl_static/soloud_sdl_static.cpp
@@ -0,0 +1,89 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+
+#include "soloud.h"
+
+#if !defined(WITH_SDL_STATIC)
+
+namespace SoLoud
+{
+ result sdlstatic_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+}
+
+#else
+
+#if defined(_MSC_VER)
+#include
+#else
+#include
+#endif
+#include
+
+
+namespace SoLoud
+{
+ static SDL_AudioSpec gActiveAudioSpec;
+ void soloud_sdlstatic_audiomixer(void *userdata, Uint8 *stream, int len)
+ {
+ short *buf = (short*)stream;
+ SoLoud::Soloud *soloud = (SoLoud::Soloud *)userdata;
+ int samples = len / (gActiveAudioSpec.channels * sizeof(short));
+ soloud->mixSigned16(buf, samples);
+ }
+
+ static void soloud_sdlstatic_deinit(SoLoud::Soloud *aSoloud)
+ {
+ SDL_CloseAudio();
+ }
+
+ result sdlstatic_init(SoLoud::Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ SDL_AudioSpec as;
+ as.freq = aSamplerate;
+ as.format = AUDIO_S16;
+ as.channels = aChannels;
+ as.samples = aBuffer;
+ as.callback = soloud_sdlstatic_audiomixer;
+ as.userdata = (void*)aSoloud;
+
+ if (SDL_OpenAudio(&as, &gActiveAudioSpec) < 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+
+ aSoloud->postinit(gActiveAudioSpec.freq, gActiveAudioSpec.samples, aFlags, gActiveAudioSpec.channels);
+
+ aSoloud->mBackendCleanupFunc = soloud_sdlstatic_deinit;
+
+ SDL_PauseAudio(0);
+ aSoloud->mBackendString = "SDL1 (static)";
+ return 0;
+ }
+
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/wasapi/soloud_wasapi.cpp b/src/soloud/src/backend/wasapi/soloud_wasapi.cpp
new file mode 100644
index 0000000..5e87844
--- /dev/null
+++ b/src/soloud/src/backend/wasapi/soloud_wasapi.cpp
@@ -0,0 +1,203 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_thread.h"
+
+#if !defined(WITH_WASAPI)
+
+namespace SoLoud
+{
+ result wasapi_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+};
+
+#else
+
+#include
+#include
+#include
+
+#define SAFE_RELEASE(x) \
+ if (0 != (x)) \
+ { \
+ (x)->Release(); \
+ (x) = 0; \
+ }
+
+namespace SoLoud
+{
+ struct WASAPIData
+ {
+ IMMDeviceEnumerator *deviceEnumerator;
+ IMMDevice *device;
+ IAudioClient *audioClient;
+ IAudioRenderClient *renderClient;
+ HANDLE bufferEndEvent;
+ HANDLE audioProcessingDoneEvent;
+ Thread::ThreadHandle thread;
+ Soloud *soloud;
+ UINT32 bufferFrames;
+ int channels;
+ };
+
+ static void wasapiSubmitBuffer(WASAPIData *aData, UINT32 aFrames)
+ {
+ BYTE *buffer = 0;
+ if (FAILED(aData->renderClient->GetBuffer(aFrames, &buffer)))
+ {
+ return;
+ }
+ aData->soloud->mixSigned16((short *)buffer, aFrames);
+ aData->renderClient->ReleaseBuffer(aFrames, 0);
+ }
+
+ static void wasapiThread(LPVOID aParam)
+ {
+ WASAPIData *data = static_cast(aParam);
+ wasapiSubmitBuffer(data, data->bufferFrames);
+ data->audioClient->Start();
+ while (WAIT_OBJECT_0 != WaitForSingleObject(data->audioProcessingDoneEvent, 0))
+ {
+ WaitForSingleObject(data->bufferEndEvent, INFINITE);
+ UINT32 padding = 0;
+ if (FAILED(data->audioClient->GetCurrentPadding(&padding)))
+ {
+ continue;
+ }
+ UINT32 frames = data->bufferFrames - padding;
+ wasapiSubmitBuffer(data, frames);
+ }
+ }
+
+ static void wasapiCleanup(Soloud *aSoloud)
+ {
+ if (0 == aSoloud->mBackendData)
+ {
+ return;
+ }
+ WASAPIData *data = static_cast(aSoloud->mBackendData);
+ SetEvent(data->audioProcessingDoneEvent);
+ SetEvent(data->bufferEndEvent);
+ if (data->thread)
+ {
+ Thread::wait(data->thread);
+ Thread::release(data->thread);
+ }
+ CloseHandle(data->bufferEndEvent);
+ CloseHandle(data->audioProcessingDoneEvent);
+ if (0 != data->audioClient)
+ {
+ data->audioClient->Stop();
+ }
+ SAFE_RELEASE(data->renderClient);
+ SAFE_RELEASE(data->audioClient);
+ SAFE_RELEASE(data->device);
+ SAFE_RELEASE(data->deviceEnumerator);
+ delete data;
+ aSoloud->mBackendData = 0;
+ CoUninitialize();
+ }
+
+ result wasapi_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ return UNKNOWN_ERROR;
+ CoInitializeEx(0, COINIT_MULTITHREADED);
+ WASAPIData *data = new WASAPIData;
+ ZeroMemory(data, sizeof(WASAPIData));
+ aSoloud->mBackendData = data;
+ aSoloud->mBackendCleanupFunc = wasapiCleanup;
+
+ data->bufferEndEvent = CreateEvent(0, FALSE, FALSE, 0);
+ if (0 == data->bufferEndEvent)
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->audioProcessingDoneEvent = CreateEvent(0, FALSE, FALSE, 0);
+ if (0 == data->audioProcessingDoneEvent)
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator), 0, CLSCTX_ALL,
+ __uuidof(IMMDeviceEnumerator),
+ reinterpret_cast(&data->deviceEnumerator))))
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (FAILED(data->deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole,
+ &data->device)))
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (FAILED(data->device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, 0,
+ reinterpret_cast(&data->audioClient))))
+ {
+ return UNKNOWN_ERROR;
+ }
+ WAVEFORMATEX format;
+ ZeroMemory(&format, sizeof(WAVEFORMATEX));
+ format.nChannels = aChannels;
+ format.nSamplesPerSec = aSamplerate;
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.wBitsPerSample = sizeof(short)*8;
+ format.nBlockAlign = (format.nChannels*format.wBitsPerSample)/8;
+ format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign;
+ REFERENCE_TIME dur = static_cast(static_cast(aBuffer)
+ / (static_cast(aSamplerate)*(1.0/10000000.0)));
+ HRESULT res = data->audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+ dur, 0, &format, 0);
+ if (FAILED(res))
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->bufferFrames = 0;
+ if (FAILED(data->audioClient->GetBufferSize(&data->bufferFrames)))
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (FAILED(data->audioClient->GetService(__uuidof(IAudioRenderClient),
+ reinterpret_cast(&data->renderClient))))
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (FAILED(data->audioClient->SetEventHandle(data->bufferEndEvent)))
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->channels = format.nChannels;
+ data->soloud = aSoloud;
+ aSoloud->postinit(aSamplerate, data->bufferFrames * format.nChannels, aFlags, 2);
+ data->thread = Thread::createThread(wasapiThread, data);
+ if (0 == data->thread)
+ {
+ return UNKNOWN_ERROR;
+ }
+ aSoloud->mBackendString = "WASAPI";
+ return 0;
+ }
+};
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/winmm/soloud_winmm.cpp b/src/soloud/src/backend/winmm/soloud_winmm.cpp
new file mode 100644
index 0000000..dd78134
--- /dev/null
+++ b/src/soloud/src/backend/winmm/soloud_winmm.cpp
@@ -0,0 +1,174 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_thread.h"
+
+#if !defined(WITH_WINMM)
+
+namespace SoLoud
+{
+ result winmm_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+};
+
+#else
+
+#include
+#include
+
+#ifdef _MSC_VER
+#pragma comment(lib, "winmm.lib")
+#endif
+
+namespace SoLoud
+{
+ static const int BUFFER_COUNT = 2;
+
+ struct SoLoudWinMMData
+ {
+ AlignedFloatBuffer buffer;
+ short *sampleBuffer[BUFFER_COUNT];
+ WAVEHDR header[BUFFER_COUNT];
+ HWAVEOUT waveOut;
+ HANDLE bufferEndEvent;
+ HANDLE audioProcessingDoneEvent;
+ Soloud *soloud;
+ int samples;
+ Thread::ThreadHandle threadHandle;
+ };
+
+ static void winMMThread(LPVOID aParam)
+ {
+ SoLoudWinMMData *data = static_cast(aParam);
+ while (WAIT_OBJECT_0 != WaitForSingleObject(data->audioProcessingDoneEvent, 0))
+ {
+ for (int i=0;iheader[i].dwFlags & WHDR_INQUEUE))
+ {
+ continue;
+ }
+ short *tgtBuf = data->sampleBuffer[i];
+
+ data->soloud->mixSigned16(tgtBuf, data->samples);
+
+ if (MMSYSERR_NOERROR != waveOutWrite(data->waveOut, &data->header[i],
+ sizeof(WAVEHDR)))
+ {
+ return;
+ }
+ }
+ WaitForSingleObject(data->bufferEndEvent, INFINITE);
+ }
+ }
+
+ static void winMMCleanup(Soloud *aSoloud)
+ {
+ if (0 == aSoloud->mBackendData)
+ {
+ return;
+ }
+ SoLoudWinMMData *data = static_cast(aSoloud->mBackendData);
+ SetEvent(data->audioProcessingDoneEvent);
+ SetEvent(data->bufferEndEvent);
+ if (data->threadHandle)
+ {
+ Thread::wait(data->threadHandle);
+ Thread::release(data->threadHandle);
+ }
+ waveOutReset(data->waveOut);
+ for (int i=0;iwaveOut, &data->header[i], sizeof(WAVEHDR));
+ if (0 != data->sampleBuffer[i])
+ {
+ delete[] data->sampleBuffer[i];
+ }
+ }
+ waveOutClose(data->waveOut);
+ CloseHandle(data->audioProcessingDoneEvent);
+ CloseHandle(data->bufferEndEvent);
+ delete data;
+ aSoloud->mBackendData = 0;
+ }
+
+ result winmm_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ SoLoudWinMMData *data = new SoLoudWinMMData;
+ ZeroMemory(data, sizeof(SoLoudWinMMData));
+ aSoloud->mBackendData = data;
+ aSoloud->mBackendCleanupFunc = winMMCleanup;
+ data->samples = aBuffer;
+ data->soloud = aSoloud;
+ data->bufferEndEvent = CreateEvent(0, FALSE, FALSE, 0);
+ if (0 == data->bufferEndEvent)
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->audioProcessingDoneEvent = CreateEvent(0, FALSE, FALSE, 0);
+ if (0 == data->audioProcessingDoneEvent)
+ {
+ return UNKNOWN_ERROR;
+ }
+ WAVEFORMATEX format;
+ ZeroMemory(&format, sizeof(WAVEFORMATEX));
+ format.nChannels = aChannels;
+ format.nSamplesPerSec = aSamplerate;
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.wBitsPerSample = sizeof(short)*8;
+ format.nBlockAlign = (format.nChannels*format.wBitsPerSample)/8;
+ format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign;
+ if (MMSYSERR_NOERROR != waveOutOpen(&data->waveOut, WAVE_MAPPER, &format,
+ reinterpret_cast(data->bufferEndEvent), 0, CALLBACK_EVENT))
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->buffer.init(data->samples*format.nChannels);
+ for (int i=0;isampleBuffer[i] = new short[data->samples*format.nChannels];
+ ZeroMemory(&data->header[i], sizeof(WAVEHDR));
+ data->header[i].dwBufferLength = data->samples*sizeof(short)*format.nChannels;
+ data->header[i].lpData = reinterpret_cast(data->sampleBuffer[i]);
+ if (MMSYSERR_NOERROR != waveOutPrepareHeader(data->waveOut, &data->header[i],
+ sizeof(WAVEHDR)))
+ {
+ return UNKNOWN_ERROR;
+ }
+ }
+ aSoloud->postinit(aSamplerate, data->samples * format.nChannels, aFlags, aChannels);
+ data->threadHandle = Thread::createThread(winMMThread, data);
+ if (0 == data->threadHandle)
+ {
+ return UNKNOWN_ERROR;
+ }
+ aSoloud->mBackendString = "WinMM";
+ return 0;
+ }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/backend/xaudio2/soloud_xaudio2.cpp b/src/soloud/src/backend/xaudio2/soloud_xaudio2.cpp
new file mode 100644
index 0000000..1dd1ed9
--- /dev/null
+++ b/src/soloud/src/backend/xaudio2/soloud_xaudio2.cpp
@@ -0,0 +1,247 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_thread.h"
+
+#if !defined(WITH_XAUDIO2)
+
+namespace SoLoud
+{
+ result xaudio2_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer)
+ {
+ return NOT_IMPLEMENTED;
+ }
+};
+
+#else
+
+#include
+
+#ifdef _MSC_VER
+#include
+#pragma comment(lib, "xaudio2.lib")
+#else
+#include "backend/xaudio2/xaudio2.h"
+#endif
+
+namespace SoLoud
+{
+ static const int BUFFER_COUNT = 2;
+
+ struct XAudio2Data
+ {
+ float *buffer[BUFFER_COUNT];
+ IXAudio2 *xaudio2;
+ IXAudio2MasteringVoice *masteringVoice;
+ IXAudio2SourceVoice *sourceVoice;
+ HANDLE bufferEndEvent;
+ HANDLE audioProcessingDoneEvent;
+ class VoiceCallback *voiceCb;
+ Thread::ThreadHandle thread;
+ Soloud *soloud;
+ int samples;
+ UINT32 bufferLengthBytes;
+ };
+
+ class VoiceCallback : public IXAudio2VoiceCallback
+ {
+ public:
+ VoiceCallback(HANDLE aBufferEndEvent)
+ : IXAudio2VoiceCallback(), mBufferEndEvent(aBufferEndEvent) {}
+ virtual ~VoiceCallback() {}
+
+ private:
+ // Called just before this voice's processing pass begins.
+ void __stdcall OnVoiceProcessingPassStart(UINT32 aBytesRequired) {}
+
+ // Called just after this voice's processing pass ends.
+ void __stdcall OnVoiceProcessingPassEnd() {}
+
+ // Called when this voice has just finished playing a buffer stream
+ // (as marked with the XAUDIO2_END_OF_STREAM flag on the last buffer).
+ void __stdcall OnStreamEnd() {}
+
+ // Called when this voice is about to start processing a new buffer.
+ void __stdcall OnBufferStart(void *aBufferContext) {}
+
+ // Called when this voice has just finished processing a buffer.
+ // The buffer can now be reused or destroyed.
+ void __stdcall OnBufferEnd(void *aBufferContext)
+ {
+ SetEvent(mBufferEndEvent);
+ }
+
+ // Called when this voice has just reached the end position of a loop.
+ void __stdcall OnLoopEnd(void *aBufferContext) {}
+
+ // Called in the event of a critical error during voice processing,
+ // such as a failing xAPO or an error from the hardware XMA decoder.
+ // The voice may have to be destroyed and re-created to recover from
+ // the error. The callback arguments report which buffer was being
+ // processed when the error occurred, and its HRESULT code.
+ void __stdcall OnVoiceError(void *aBufferContext, HRESULT aError) {}
+
+ HANDLE mBufferEndEvent;
+ };
+
+ static void xaudio2Thread(LPVOID aParam)
+ {
+ XAudio2Data *data = static_cast(aParam);
+ int bufferIndex = 0;
+ while (WAIT_OBJECT_0 != WaitForSingleObject(data->audioProcessingDoneEvent, 0))
+ {
+ XAUDIO2_VOICE_STATE state;
+ data->sourceVoice->GetState(&state);
+ while (state.BuffersQueued < BUFFER_COUNT)
+ {
+ data->soloud->mix(data->buffer[bufferIndex], data->samples);
+ XAUDIO2_BUFFER info = {0};
+ info.AudioBytes = data->bufferLengthBytes;
+ info.pAudioData = reinterpret_cast(data->buffer[bufferIndex]);
+ data->sourceVoice->SubmitSourceBuffer(&info);
+ ++bufferIndex;
+ if (bufferIndex >= BUFFER_COUNT)
+ {
+ bufferIndex = 0;
+ }
+ data->sourceVoice->GetState(&state);
+ }
+ WaitForSingleObject(data->bufferEndEvent, INFINITE);
+ }
+ }
+
+ static void xaudio2Cleanup(Soloud *aSoloud)
+ {
+ if (0 == aSoloud->mBackendData)
+ {
+ return;
+ }
+ XAudio2Data *data = static_cast(aSoloud->mBackendData);
+ SetEvent(data->audioProcessingDoneEvent);
+ SetEvent(data->bufferEndEvent);
+ Thread::wait(data->thread);
+ Thread::release(data->thread);
+ if (0 != data->sourceVoice)
+ {
+ data->sourceVoice->Stop();
+ data->sourceVoice->FlushSourceBuffers();
+ }
+ if (0 != data->xaudio2)
+ {
+ data->xaudio2->StopEngine();
+ }
+ if (0 != data->sourceVoice)
+ {
+ data->sourceVoice->DestroyVoice();
+ }
+ if (0 != data->voiceCb)
+ {
+ delete data->voiceCb;
+ }
+ if (0 != data->masteringVoice)
+ {
+ data->masteringVoice->DestroyVoice();
+ }
+ if (0 != data->xaudio2)
+ {
+ data->xaudio2->Release();
+ }
+ for (int i=0;ibuffer[i])
+ {
+ delete[] data->buffer[i];
+ }
+ }
+ CloseHandle(data->bufferEndEvent);
+ CloseHandle(data->audioProcessingDoneEvent);
+ delete data;
+ aSoloud->mBackendData = 0;
+ CoUninitialize();
+ }
+
+ result xaudio2_init(Soloud *aSoloud, unsigned int aFlags, unsigned int aSamplerate, unsigned int aBuffer, unsigned int aChannels)
+ {
+ if (FAILED(CoInitializeEx(0, COINIT_MULTITHREADED)))
+ {
+ return UNKNOWN_ERROR;
+ }
+ XAudio2Data *data = new XAudio2Data;
+ ZeroMemory(data, sizeof(XAudio2Data));
+ aSoloud->mBackendData = data;
+ aSoloud->mBackendCleanupFunc = xaudio2Cleanup;
+ data->bufferEndEvent = CreateEvent(0, FALSE, FALSE, 0);
+ if (0 == data->bufferEndEvent)
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->audioProcessingDoneEvent = CreateEvent(0, FALSE, FALSE, 0);
+ if (0 == data->audioProcessingDoneEvent)
+ {
+ return UNKNOWN_ERROR;
+ }
+ WAVEFORMATEX format;
+ ZeroMemory(&format, sizeof(WAVEFORMATEX));
+ format.nChannels = 2;
+ format.nSamplesPerSec = aSamplerate;
+ format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+ format.nAvgBytesPerSec = aSamplerate*sizeof(float)*format.nChannels;
+ format.nBlockAlign = sizeof(float)*format.nChannels;
+ format.wBitsPerSample = sizeof(float)*8;
+ if (FAILED(XAudio2Create(&data->xaudio2)))
+ {
+ return UNKNOWN_ERROR;
+ }
+ if (FAILED(data->xaudio2->CreateMasteringVoice(&data->masteringVoice,
+ format.nChannels, aSamplerate)))
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->voiceCb = new VoiceCallback(data->bufferEndEvent);
+ if (FAILED(data->xaudio2->CreateSourceVoice(&data->sourceVoice,
+ &format, XAUDIO2_VOICE_NOSRC|XAUDIO2_VOICE_NOPITCH, 2.f, data->voiceCb)))
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->bufferLengthBytes = aBuffer * format.nChannels * sizeof(float);
+ for (int i=0;ibuffer[i] = new float[aBuffer * format.nChannels];
+ }
+ data->samples = aBuffer;
+ data->soloud = aSoloud;
+ aSoloud->postinit(aSamplerate, aBuffer * format.nChannels, aFlags, 2);
+ data->thread = Thread::createThread(xaudio2Thread, data);
+ if (0 == data->thread)
+ {
+ return UNKNOWN_ERROR;
+ }
+ data->sourceVoice->Start();
+ aSoloud->mBackendString = "XAudio2";
+ return 0;
+ }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/soloud/src/c_api/soloud.def b/src/soloud/src/c_api/soloud.def
new file mode 100644
index 0000000..490fc3f
--- /dev/null
+++ b/src/soloud/src/c_api/soloud.def
@@ -0,0 +1,395 @@
+EXPORTS
+ Soloud_destroy
+ Soloud_create
+ Soloud_init
+ Soloud_initEx
+ Soloud_deinit
+ Soloud_getVersion
+ Soloud_getErrorString
+ Soloud_getBackendId
+ Soloud_getBackendString
+ Soloud_getBackendChannels
+ Soloud_getBackendSamplerate
+ Soloud_getBackendBufferSize
+ Soloud_setSpeakerPosition
+ Soloud_getSpeakerPosition
+ Soloud_play
+ Soloud_playEx
+ Soloud_playClocked
+ Soloud_playClockedEx
+ Soloud_play3d
+ Soloud_play3dEx
+ Soloud_play3dClocked
+ Soloud_play3dClockedEx
+ Soloud_playBackground
+ Soloud_playBackgroundEx
+ Soloud_seek
+ Soloud_stop
+ Soloud_stopAll
+ Soloud_stopAudioSource
+ Soloud_countAudioSource
+ Soloud_setFilterParameter
+ Soloud_getFilterParameter
+ Soloud_fadeFilterParameter
+ Soloud_oscillateFilterParameter
+ Soloud_getStreamTime
+ Soloud_getStreamPosition
+ Soloud_getPause
+ Soloud_getVolume
+ Soloud_getOverallVolume
+ Soloud_getPan
+ Soloud_getSamplerate
+ Soloud_getProtectVoice
+ Soloud_getActiveVoiceCount
+ Soloud_getVoiceCount
+ Soloud_isValidVoiceHandle
+ Soloud_getRelativePlaySpeed
+ Soloud_getPostClipScaler
+ Soloud_getGlobalVolume
+ Soloud_getMaxActiveVoiceCount
+ Soloud_getLooping
+ Soloud_getLoopPoint
+ Soloud_setLoopPoint
+ Soloud_setLooping
+ Soloud_setMaxActiveVoiceCount
+ Soloud_setInaudibleBehavior
+ Soloud_setGlobalVolume
+ Soloud_setPostClipScaler
+ Soloud_setPause
+ Soloud_setPauseAll
+ Soloud_setRelativePlaySpeed
+ Soloud_setProtectVoice
+ Soloud_setSamplerate
+ Soloud_setPan
+ Soloud_setPanAbsolute
+ Soloud_setPanAbsoluteEx
+ Soloud_setVolume
+ Soloud_setDelaySamples
+ Soloud_fadeVolume
+ Soloud_fadePan
+ Soloud_fadeRelativePlaySpeed
+ Soloud_fadeGlobalVolume
+ Soloud_schedulePause
+ Soloud_scheduleStop
+ Soloud_oscillateVolume
+ Soloud_oscillatePan
+ Soloud_oscillateRelativePlaySpeed
+ Soloud_oscillateGlobalVolume
+ Soloud_setGlobalFilter
+ Soloud_setVisualizationEnable
+ Soloud_calcFFT
+ Soloud_getWave
+ Soloud_getApproximateVolume
+ Soloud_getLoopCount
+ Soloud_getInfo
+ Soloud_createVoiceGroup
+ Soloud_destroyVoiceGroup
+ Soloud_addVoiceToGroup
+ Soloud_isVoiceGroup
+ Soloud_isVoiceGroupEmpty
+ Soloud_update3dAudio
+ Soloud_set3dSoundSpeed
+ Soloud_get3dSoundSpeed
+ Soloud_set3dListenerParameters
+ Soloud_set3dListenerParametersEx
+ Soloud_set3dListenerPosition
+ Soloud_set3dListenerAt
+ Soloud_set3dListenerUp
+ Soloud_set3dListenerVelocity
+ Soloud_set3dSourceParameters
+ Soloud_set3dSourceParametersEx
+ Soloud_set3dSourcePosition
+ Soloud_set3dSourceVelocity
+ Soloud_set3dSourceMinMaxDistance
+ Soloud_set3dSourceAttenuation
+ Soloud_set3dSourceDopplerFactor
+ Soloud_mix
+ Soloud_mixSigned16
+ AudioAttenuator_destroy
+ AudioAttenuator_attenuate
+ BassboostFilter_destroy
+ BassboostFilter_setParams
+ BassboostFilter_create
+ BiquadResonantFilter_destroy
+ BiquadResonantFilter_create
+ BiquadResonantFilter_setParams
+ Bus_destroy
+ Bus_create
+ Bus_setFilter
+ Bus_play
+ Bus_playEx
+ Bus_playClocked
+ Bus_playClockedEx
+ Bus_play3d
+ Bus_play3dEx
+ Bus_play3dClocked
+ Bus_play3dClockedEx
+ Bus_setChannels
+ Bus_setVisualizationEnable
+ Bus_calcFFT
+ Bus_getWave
+ Bus_getApproximateVolume
+ Bus_setVolume
+ Bus_setLooping
+ Bus_set3dMinMaxDistance
+ Bus_set3dAttenuation
+ Bus_set3dDopplerFactor
+ Bus_set3dListenerRelative
+ Bus_set3dDistanceDelay
+ Bus_set3dCollider
+ Bus_set3dColliderEx
+ Bus_set3dAttenuator
+ Bus_setInaudibleBehavior
+ Bus_setLoopPoint
+ Bus_getLoopPoint
+ Bus_stop
+ DCRemovalFilter_destroy
+ DCRemovalFilter_create
+ DCRemovalFilter_setParams
+ DCRemovalFilter_setParamsEx
+ EchoFilter_destroy
+ EchoFilter_create
+ EchoFilter_setParams
+ EchoFilter_setParamsEx
+ FFTFilter_destroy
+ FFTFilter_create
+ FlangerFilter_destroy
+ FlangerFilter_create
+ FlangerFilter_setParams
+ LofiFilter_destroy
+ LofiFilter_create
+ LofiFilter_setParams
+ Monotone_destroy
+ Monotone_create
+ Monotone_setParams
+ Monotone_setParamsEx
+ Monotone_load
+ Monotone_loadMem
+ Monotone_loadMemEx
+ Monotone_loadFile
+ Monotone_setVolume
+ Monotone_setLooping
+ Monotone_set3dMinMaxDistance
+ Monotone_set3dAttenuation
+ Monotone_set3dDopplerFactor
+ Monotone_set3dListenerRelative
+ Monotone_set3dDistanceDelay
+ Monotone_set3dCollider
+ Monotone_set3dColliderEx
+ Monotone_set3dAttenuator
+ Monotone_setInaudibleBehavior
+ Monotone_setLoopPoint
+ Monotone_getLoopPoint
+ Monotone_setFilter
+ Monotone_stop
+ Openmpt_destroy
+ Openmpt_create
+ Openmpt_load
+ Openmpt_loadMem
+ Openmpt_loadMemEx
+ Openmpt_loadFile
+ Openmpt_setVolume
+ Openmpt_setLooping
+ Openmpt_set3dMinMaxDistance
+ Openmpt_set3dAttenuation
+ Openmpt_set3dDopplerFactor
+ Openmpt_set3dListenerRelative
+ Openmpt_set3dDistanceDelay
+ Openmpt_set3dCollider
+ Openmpt_set3dColliderEx
+ Openmpt_set3dAttenuator
+ Openmpt_setInaudibleBehavior
+ Openmpt_setLoopPoint
+ Openmpt_getLoopPoint
+ Openmpt_setFilter
+ Openmpt_stop
+ Queue_destroy
+ Queue_create
+ Queue_play
+ Queue_getQueueCount
+ Queue_isCurrentlyPlaying
+ Queue_setParamsFromAudioSource
+ Queue_setParams
+ Queue_setParamsEx
+ Queue_setVolume
+ Queue_setLooping
+ Queue_set3dMinMaxDistance
+ Queue_set3dAttenuation
+ Queue_set3dDopplerFactor
+ Queue_set3dListenerRelative
+ Queue_set3dDistanceDelay
+ Queue_set3dCollider
+ Queue_set3dColliderEx
+ Queue_set3dAttenuator
+ Queue_setInaudibleBehavior
+ Queue_setLoopPoint
+ Queue_getLoopPoint
+ Queue_setFilter
+ Queue_stop
+ RobotizeFilter_destroy
+ RobotizeFilter_create
+ Prg_destroy
+ Prg_create
+ Prg_rand
+ Prg_srand
+ Sfxr_destroy
+ Sfxr_create
+ Sfxr_resetParams
+ Sfxr_loadParams
+ Sfxr_loadParamsMem
+ Sfxr_loadParamsMemEx
+ Sfxr_loadParamsFile
+ Sfxr_loadPreset
+ Sfxr_setVolume
+ Sfxr_setLooping
+ Sfxr_set3dMinMaxDistance
+ Sfxr_set3dAttenuation
+ Sfxr_set3dDopplerFactor
+ Sfxr_set3dListenerRelative
+ Sfxr_set3dDistanceDelay
+ Sfxr_set3dCollider
+ Sfxr_set3dColliderEx
+ Sfxr_set3dAttenuator
+ Sfxr_setInaudibleBehavior
+ Sfxr_setLoopPoint
+ Sfxr_getLoopPoint
+ Sfxr_setFilter
+ Sfxr_stop
+ Speech_destroy
+ Speech_create
+ Speech_setText
+ Speech_setParams
+ Speech_setParamsEx
+ Speech_setVolume
+ Speech_setLooping
+ Speech_set3dMinMaxDistance
+ Speech_set3dAttenuation
+ Speech_set3dDopplerFactor
+ Speech_set3dListenerRelative
+ Speech_set3dDistanceDelay
+ Speech_set3dCollider
+ Speech_set3dColliderEx
+ Speech_set3dAttenuator
+ Speech_setInaudibleBehavior
+ Speech_setLoopPoint
+ Speech_getLoopPoint
+ Speech_setFilter
+ Speech_stop
+ TedSid_destroy
+ TedSid_create
+ TedSid_load
+ TedSid_loadToMem
+ TedSid_loadMem
+ TedSid_loadMemEx
+ TedSid_loadFileToMem
+ TedSid_loadFile
+ TedSid_setVolume
+ TedSid_setLooping
+ TedSid_set3dMinMaxDistance
+ TedSid_set3dAttenuation
+ TedSid_set3dDopplerFactor
+ TedSid_set3dListenerRelative
+ TedSid_set3dDistanceDelay
+ TedSid_set3dCollider
+ TedSid_set3dColliderEx
+ TedSid_set3dAttenuator
+ TedSid_setInaudibleBehavior
+ TedSid_setLoopPoint
+ TedSid_getLoopPoint
+ TedSid_setFilter
+ TedSid_stop
+ Vic_destroy
+ Vic_create
+ Vic_setModel
+ Vic_getModel
+ Vic_setRegister
+ Vic_getRegister
+ Vic_setVolume
+ Vic_setLooping
+ Vic_set3dMinMaxDistance
+ Vic_set3dAttenuation
+ Vic_set3dDopplerFactor
+ Vic_set3dListenerRelative
+ Vic_set3dDistanceDelay
+ Vic_set3dCollider
+ Vic_set3dColliderEx
+ Vic_set3dAttenuator
+ Vic_setInaudibleBehavior
+ Vic_setLoopPoint
+ Vic_getLoopPoint
+ Vic_setFilter
+ Vic_stop
+ Vizsn_destroy
+ Vizsn_create
+ Vizsn_setText
+ Vizsn_setVolume
+ Vizsn_setLooping
+ Vizsn_set3dMinMaxDistance
+ Vizsn_set3dAttenuation
+ Vizsn_set3dDopplerFactor
+ Vizsn_set3dListenerRelative
+ Vizsn_set3dDistanceDelay
+ Vizsn_set3dCollider
+ Vizsn_set3dColliderEx
+ Vizsn_set3dAttenuator
+ Vizsn_setInaudibleBehavior
+ Vizsn_setLoopPoint
+ Vizsn_getLoopPoint
+ Vizsn_setFilter
+ Vizsn_stop
+ Wav_destroy
+ Wav_create
+ Wav_load
+ Wav_loadMem
+ Wav_loadMemEx
+ Wav_loadFile
+ Wav_loadRawWave8
+ Wav_loadRawWave8Ex
+ Wav_loadRawWave16
+ Wav_loadRawWave16Ex
+ Wav_loadRawWave
+ Wav_loadRawWaveEx
+ Wav_getLength
+ Wav_setVolume
+ Wav_setLooping
+ Wav_set3dMinMaxDistance
+ Wav_set3dAttenuation
+ Wav_set3dDopplerFactor
+ Wav_set3dListenerRelative
+ Wav_set3dDistanceDelay
+ Wav_set3dCollider
+ Wav_set3dColliderEx
+ Wav_set3dAttenuator
+ Wav_setInaudibleBehavior
+ Wav_setLoopPoint
+ Wav_getLoopPoint
+ Wav_setFilter
+ Wav_stop
+ WaveShaperFilter_destroy
+ WaveShaperFilter_setParams
+ WaveShaperFilter_setParamsEx
+ WaveShaperFilter_create
+ WavStream_destroy
+ WavStream_create
+ WavStream_load
+ WavStream_loadMem
+ WavStream_loadMemEx
+ WavStream_loadToMem
+ WavStream_loadFile
+ WavStream_loadFileToMem
+ WavStream_getLength
+ WavStream_setVolume
+ WavStream_setLooping
+ WavStream_set3dMinMaxDistance
+ WavStream_set3dAttenuation
+ WavStream_set3dDopplerFactor
+ WavStream_set3dListenerRelative
+ WavStream_set3dDistanceDelay
+ WavStream_set3dCollider
+ WavStream_set3dColliderEx
+ WavStream_set3dAttenuator
+ WavStream_setInaudibleBehavior
+ WavStream_setLoopPoint
+ WavStream_getLoopPoint
+ WavStream_setFilter
+ WavStream_stop
diff --git a/src/soloud/src/c_api/soloud_c.cpp b/src/soloud/src/c_api/soloud_c.cpp
new file mode 100644
index 0000000..068c1c1
--- /dev/null
+++ b/src/soloud/src/c_api/soloud_c.cpp
@@ -0,0 +1,2383 @@
+/* **************************************************
+ * WARNING: this is a generated file. Do not edit. *
+ * Any edits will be overwritten by the generator. *
+ ************************************************** */
+
+/*
+SoLoud audio engine
+Copyright (c) 2013-2016 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/* SoLoud C-Api Code Generator (c)2013-2018 Jari Komppa http://iki.fi/sol/ */
+
+#include "../include/soloud.h"
+#include "../include/soloud_audiosource.h"
+#include "../include/soloud_bassboostfilter.h"
+#include "../include/soloud_biquadresonantfilter.h"
+#include "../include/soloud_bus.h"
+#include "../include/soloud_dcremovalfilter.h"
+#include "../include/soloud_echofilter.h"
+#include "../include/soloud_fader.h"
+#include "../include/soloud_fft.h"
+#include "../include/soloud_fftfilter.h"
+#include "../include/soloud_filter.h"
+#include "../include/soloud_flangerfilter.h"
+#include "../include/soloud_lofifilter.h"
+#include "../include/soloud_monotone.h"
+#include "../include/soloud_openmpt.h"
+#include "../include/soloud_queue.h"
+#include "../include/soloud_robotizefilter.h"
+#include "../include/soloud_sfxr.h"
+#include "../include/soloud_speech.h"
+#include "../include/soloud_tedsid.h"
+#include "../include/soloud_vic.h"
+#include "../include/soloud_vizsn.h"
+#include "../include/soloud_wav.h"
+#include "../include/soloud_waveshaperfilter.h"
+#include "../include/soloud_wavstream.h"
+
+using namespace SoLoud;
+
+extern "C"
+{
+
+void Soloud_destroy(void * aClassPtr)
+{
+ delete (Soloud *)aClassPtr;
+}
+
+void * Soloud_create()
+{
+ return (void *)new Soloud;
+}
+
+int Soloud_init(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->init();
+}
+
+int Soloud_initEx(void * aClassPtr, unsigned int aFlags, unsigned int aBackend, unsigned int aSamplerate, unsigned int aBufferSize, unsigned int aChannels)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->init(aFlags, aBackend, aSamplerate, aBufferSize, aChannels);
+}
+
+void Soloud_deinit(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->deinit();
+}
+
+unsigned int Soloud_getVersion(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getVersion();
+}
+
+const char * Soloud_getErrorString(void * aClassPtr, int aErrorCode)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getErrorString(aErrorCode);
+}
+
+unsigned int Soloud_getBackendId(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getBackendId();
+}
+
+const char * Soloud_getBackendString(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getBackendString();
+}
+
+unsigned int Soloud_getBackendChannels(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getBackendChannels();
+}
+
+unsigned int Soloud_getBackendSamplerate(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getBackendSamplerate();
+}
+
+unsigned int Soloud_getBackendBufferSize(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getBackendBufferSize();
+}
+
+int Soloud_setSpeakerPosition(void * aClassPtr, unsigned int aChannel, float aX, float aY, float aZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->setSpeakerPosition(aChannel, aX, aY, aZ);
+}
+
+int Soloud_getSpeakerPosition(void * aClassPtr, unsigned int aChannel, float * aX, float * aY, float * aZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getSpeakerPosition(aChannel, *aX, *aY, *aZ);
+}
+
+unsigned int Soloud_play(void * aClassPtr, AudioSource * aSound)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->play(*aSound);
+}
+
+unsigned int Soloud_playEx(void * aClassPtr, AudioSource * aSound, float aVolume, float aPan, int aPaused, unsigned int aBus)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->play(*aSound, aVolume, aPan, !!aPaused, aBus);
+}
+
+unsigned int Soloud_playClocked(void * aClassPtr, double aSoundTime, AudioSource * aSound)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->playClocked(aSoundTime, *aSound);
+}
+
+unsigned int Soloud_playClockedEx(void * aClassPtr, double aSoundTime, AudioSource * aSound, float aVolume, float aPan, unsigned int aBus)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->playClocked(aSoundTime, *aSound, aVolume, aPan, aBus);
+}
+
+unsigned int Soloud_play3d(void * aClassPtr, AudioSource * aSound, float aPosX, float aPosY, float aPosZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->play3d(*aSound, aPosX, aPosY, aPosZ);
+}
+
+unsigned int Soloud_play3dEx(void * aClassPtr, AudioSource * aSound, float aPosX, float aPosY, float aPosZ, float aVelX, float aVelY, float aVelZ, float aVolume, int aPaused, unsigned int aBus)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->play3d(*aSound, aPosX, aPosY, aPosZ, aVelX, aVelY, aVelZ, aVolume, !!aPaused, aBus);
+}
+
+unsigned int Soloud_play3dClocked(void * aClassPtr, double aSoundTime, AudioSource * aSound, float aPosX, float aPosY, float aPosZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->play3dClocked(aSoundTime, *aSound, aPosX, aPosY, aPosZ);
+}
+
+unsigned int Soloud_play3dClockedEx(void * aClassPtr, double aSoundTime, AudioSource * aSound, float aPosX, float aPosY, float aPosZ, float aVelX, float aVelY, float aVelZ, float aVolume, unsigned int aBus)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->play3dClocked(aSoundTime, *aSound, aPosX, aPosY, aPosZ, aVelX, aVelY, aVelZ, aVolume, aBus);
+}
+
+unsigned int Soloud_playBackground(void * aClassPtr, AudioSource * aSound)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->playBackground(*aSound);
+}
+
+unsigned int Soloud_playBackgroundEx(void * aClassPtr, AudioSource * aSound, float aVolume, int aPaused, unsigned int aBus)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->playBackground(*aSound, aVolume, !!aPaused, aBus);
+}
+
+int Soloud_seek(void * aClassPtr, unsigned int aVoiceHandle, double aSeconds)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->seek(aVoiceHandle, aSeconds);
+}
+
+void Soloud_stop(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->stop(aVoiceHandle);
+}
+
+void Soloud_stopAll(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->stopAll();
+}
+
+void Soloud_stopAudioSource(void * aClassPtr, AudioSource * aSound)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->stopAudioSource(*aSound);
+}
+
+int Soloud_countAudioSource(void * aClassPtr, AudioSource * aSound)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->countAudioSource(*aSound);
+}
+
+void Soloud_setFilterParameter(void * aClassPtr, unsigned int aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aValue)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setFilterParameter(aVoiceHandle, aFilterId, aAttributeId, aValue);
+}
+
+float Soloud_getFilterParameter(void * aClassPtr, unsigned int aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getFilterParameter(aVoiceHandle, aFilterId, aAttributeId);
+}
+
+void Soloud_fadeFilterParameter(void * aClassPtr, unsigned int aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->fadeFilterParameter(aVoiceHandle, aFilterId, aAttributeId, aTo, aTime);
+}
+
+void Soloud_oscillateFilterParameter(void * aClassPtr, unsigned int aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aFrom, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->oscillateFilterParameter(aVoiceHandle, aFilterId, aAttributeId, aFrom, aTo, aTime);
+}
+
+double Soloud_getStreamTime(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getStreamTime(aVoiceHandle);
+}
+
+double Soloud_getStreamPosition(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getStreamPosition(aVoiceHandle);
+}
+
+int Soloud_getPause(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getPause(aVoiceHandle);
+}
+
+float Soloud_getVolume(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getVolume(aVoiceHandle);
+}
+
+float Soloud_getOverallVolume(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getOverallVolume(aVoiceHandle);
+}
+
+float Soloud_getPan(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getPan(aVoiceHandle);
+}
+
+float Soloud_getSamplerate(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getSamplerate(aVoiceHandle);
+}
+
+int Soloud_getProtectVoice(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getProtectVoice(aVoiceHandle);
+}
+
+unsigned int Soloud_getActiveVoiceCount(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getActiveVoiceCount();
+}
+
+unsigned int Soloud_getVoiceCount(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getVoiceCount();
+}
+
+int Soloud_isValidVoiceHandle(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->isValidVoiceHandle(aVoiceHandle);
+}
+
+float Soloud_getRelativePlaySpeed(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getRelativePlaySpeed(aVoiceHandle);
+}
+
+float Soloud_getPostClipScaler(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getPostClipScaler();
+}
+
+float Soloud_getGlobalVolume(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getGlobalVolume();
+}
+
+unsigned int Soloud_getMaxActiveVoiceCount(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getMaxActiveVoiceCount();
+}
+
+int Soloud_getLooping(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getLooping(aVoiceHandle);
+}
+
+double Soloud_getLoopPoint(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getLoopPoint(aVoiceHandle);
+}
+
+void Soloud_setLoopPoint(void * aClassPtr, unsigned int aVoiceHandle, double aLoopPoint)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setLoopPoint(aVoiceHandle, aLoopPoint);
+}
+
+void Soloud_setLooping(void * aClassPtr, unsigned int aVoiceHandle, int aLooping)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setLooping(aVoiceHandle, !!aLooping);
+}
+
+int Soloud_setMaxActiveVoiceCount(void * aClassPtr, unsigned int aVoiceCount)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->setMaxActiveVoiceCount(aVoiceCount);
+}
+
+void Soloud_setInaudibleBehavior(void * aClassPtr, unsigned int aVoiceHandle, int aMustTick, int aKill)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setInaudibleBehavior(aVoiceHandle, !!aMustTick, !!aKill);
+}
+
+void Soloud_setGlobalVolume(void * aClassPtr, float aVolume)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setGlobalVolume(aVolume);
+}
+
+void Soloud_setPostClipScaler(void * aClassPtr, float aScaler)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setPostClipScaler(aScaler);
+}
+
+void Soloud_setPause(void * aClassPtr, unsigned int aVoiceHandle, int aPause)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setPause(aVoiceHandle, !!aPause);
+}
+
+void Soloud_setPauseAll(void * aClassPtr, int aPause)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setPauseAll(!!aPause);
+}
+
+int Soloud_setRelativePlaySpeed(void * aClassPtr, unsigned int aVoiceHandle, float aSpeed)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->setRelativePlaySpeed(aVoiceHandle, aSpeed);
+}
+
+void Soloud_setProtectVoice(void * aClassPtr, unsigned int aVoiceHandle, int aProtect)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setProtectVoice(aVoiceHandle, !!aProtect);
+}
+
+void Soloud_setSamplerate(void * aClassPtr, unsigned int aVoiceHandle, float aSamplerate)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setSamplerate(aVoiceHandle, aSamplerate);
+}
+
+void Soloud_setPan(void * aClassPtr, unsigned int aVoiceHandle, float aPan)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setPan(aVoiceHandle, aPan);
+}
+
+void Soloud_setPanAbsolute(void * aClassPtr, unsigned int aVoiceHandle, float aLVolume, float aRVolume)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setPanAbsolute(aVoiceHandle, aLVolume, aRVolume);
+}
+
+void Soloud_setPanAbsoluteEx(void * aClassPtr, unsigned int aVoiceHandle, float aLVolume, float aRVolume, float aLBVolume, float aRBVolume, float aCVolume, float aSVolume)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setPanAbsolute(aVoiceHandle, aLVolume, aRVolume, aLBVolume, aRBVolume, aCVolume, aSVolume);
+}
+
+void Soloud_setVolume(void * aClassPtr, unsigned int aVoiceHandle, float aVolume)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setVolume(aVoiceHandle, aVolume);
+}
+
+void Soloud_setDelaySamples(void * aClassPtr, unsigned int aVoiceHandle, unsigned int aSamples)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setDelaySamples(aVoiceHandle, aSamples);
+}
+
+void Soloud_fadeVolume(void * aClassPtr, unsigned int aVoiceHandle, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->fadeVolume(aVoiceHandle, aTo, aTime);
+}
+
+void Soloud_fadePan(void * aClassPtr, unsigned int aVoiceHandle, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->fadePan(aVoiceHandle, aTo, aTime);
+}
+
+void Soloud_fadeRelativePlaySpeed(void * aClassPtr, unsigned int aVoiceHandle, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->fadeRelativePlaySpeed(aVoiceHandle, aTo, aTime);
+}
+
+void Soloud_fadeGlobalVolume(void * aClassPtr, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->fadeGlobalVolume(aTo, aTime);
+}
+
+void Soloud_schedulePause(void * aClassPtr, unsigned int aVoiceHandle, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->schedulePause(aVoiceHandle, aTime);
+}
+
+void Soloud_scheduleStop(void * aClassPtr, unsigned int aVoiceHandle, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->scheduleStop(aVoiceHandle, aTime);
+}
+
+void Soloud_oscillateVolume(void * aClassPtr, unsigned int aVoiceHandle, float aFrom, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->oscillateVolume(aVoiceHandle, aFrom, aTo, aTime);
+}
+
+void Soloud_oscillatePan(void * aClassPtr, unsigned int aVoiceHandle, float aFrom, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->oscillatePan(aVoiceHandle, aFrom, aTo, aTime);
+}
+
+void Soloud_oscillateRelativePlaySpeed(void * aClassPtr, unsigned int aVoiceHandle, float aFrom, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->oscillateRelativePlaySpeed(aVoiceHandle, aFrom, aTo, aTime);
+}
+
+void Soloud_oscillateGlobalVolume(void * aClassPtr, float aFrom, float aTo, double aTime)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->oscillateGlobalVolume(aFrom, aTo, aTime);
+}
+
+void Soloud_setGlobalFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setGlobalFilter(aFilterId, aFilter);
+}
+
+void Soloud_setVisualizationEnable(void * aClassPtr, int aEnable)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->setVisualizationEnable(!!aEnable);
+}
+
+float * Soloud_calcFFT(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->calcFFT();
+}
+
+float * Soloud_getWave(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getWave();
+}
+
+float Soloud_getApproximateVolume(void * aClassPtr, unsigned int aChannel)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getApproximateVolume(aChannel);
+}
+
+unsigned int Soloud_getLoopCount(void * aClassPtr, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getLoopCount(aVoiceHandle);
+}
+
+float Soloud_getInfo(void * aClassPtr, unsigned int aVoiceHandle, unsigned int aInfoKey)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->getInfo(aVoiceHandle, aInfoKey);
+}
+
+unsigned int Soloud_createVoiceGroup(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->createVoiceGroup();
+}
+
+int Soloud_destroyVoiceGroup(void * aClassPtr, unsigned int aVoiceGroupHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->destroyVoiceGroup(aVoiceGroupHandle);
+}
+
+int Soloud_addVoiceToGroup(void * aClassPtr, unsigned int aVoiceGroupHandle, unsigned int aVoiceHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->addVoiceToGroup(aVoiceGroupHandle, aVoiceHandle);
+}
+
+int Soloud_isVoiceGroup(void * aClassPtr, unsigned int aVoiceGroupHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->isVoiceGroup(aVoiceGroupHandle);
+}
+
+int Soloud_isVoiceGroupEmpty(void * aClassPtr, unsigned int aVoiceGroupHandle)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->isVoiceGroupEmpty(aVoiceGroupHandle);
+}
+
+void Soloud_update3dAudio(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->update3dAudio();
+}
+
+int Soloud_set3dSoundSpeed(void * aClassPtr, float aSpeed)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->set3dSoundSpeed(aSpeed);
+}
+
+float Soloud_get3dSoundSpeed(void * aClassPtr)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ return cl->get3dSoundSpeed();
+}
+
+void Soloud_set3dListenerParameters(void * aClassPtr, float aPosX, float aPosY, float aPosZ, float aAtX, float aAtY, float aAtZ, float aUpX, float aUpY, float aUpZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dListenerParameters(aPosX, aPosY, aPosZ, aAtX, aAtY, aAtZ, aUpX, aUpY, aUpZ);
+}
+
+void Soloud_set3dListenerParametersEx(void * aClassPtr, float aPosX, float aPosY, float aPosZ, float aAtX, float aAtY, float aAtZ, float aUpX, float aUpY, float aUpZ, float aVelocityX, float aVelocityY, float aVelocityZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dListenerParameters(aPosX, aPosY, aPosZ, aAtX, aAtY, aAtZ, aUpX, aUpY, aUpZ, aVelocityX, aVelocityY, aVelocityZ);
+}
+
+void Soloud_set3dListenerPosition(void * aClassPtr, float aPosX, float aPosY, float aPosZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dListenerPosition(aPosX, aPosY, aPosZ);
+}
+
+void Soloud_set3dListenerAt(void * aClassPtr, float aAtX, float aAtY, float aAtZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dListenerAt(aAtX, aAtY, aAtZ);
+}
+
+void Soloud_set3dListenerUp(void * aClassPtr, float aUpX, float aUpY, float aUpZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dListenerUp(aUpX, aUpY, aUpZ);
+}
+
+void Soloud_set3dListenerVelocity(void * aClassPtr, float aVelocityX, float aVelocityY, float aVelocityZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dListenerVelocity(aVelocityX, aVelocityY, aVelocityZ);
+}
+
+void Soloud_set3dSourceParameters(void * aClassPtr, unsigned int aVoiceHandle, float aPosX, float aPosY, float aPosZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dSourceParameters(aVoiceHandle, aPosX, aPosY, aPosZ);
+}
+
+void Soloud_set3dSourceParametersEx(void * aClassPtr, unsigned int aVoiceHandle, float aPosX, float aPosY, float aPosZ, float aVelocityX, float aVelocityY, float aVelocityZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dSourceParameters(aVoiceHandle, aPosX, aPosY, aPosZ, aVelocityX, aVelocityY, aVelocityZ);
+}
+
+void Soloud_set3dSourcePosition(void * aClassPtr, unsigned int aVoiceHandle, float aPosX, float aPosY, float aPosZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dSourcePosition(aVoiceHandle, aPosX, aPosY, aPosZ);
+}
+
+void Soloud_set3dSourceVelocity(void * aClassPtr, unsigned int aVoiceHandle, float aVelocityX, float aVelocityY, float aVelocityZ)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dSourceVelocity(aVoiceHandle, aVelocityX, aVelocityY, aVelocityZ);
+}
+
+void Soloud_set3dSourceMinMaxDistance(void * aClassPtr, unsigned int aVoiceHandle, float aMinDistance, float aMaxDistance)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dSourceMinMaxDistance(aVoiceHandle, aMinDistance, aMaxDistance);
+}
+
+void Soloud_set3dSourceAttenuation(void * aClassPtr, unsigned int aVoiceHandle, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dSourceAttenuation(aVoiceHandle, aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Soloud_set3dSourceDopplerFactor(void * aClassPtr, unsigned int aVoiceHandle, float aDopplerFactor)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->set3dSourceDopplerFactor(aVoiceHandle, aDopplerFactor);
+}
+
+void Soloud_mix(void * aClassPtr, float * aBuffer, unsigned int aSamples)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->mix(aBuffer, aSamples);
+}
+
+void Soloud_mixSigned16(void * aClassPtr, short * aBuffer, unsigned int aSamples)
+{
+ Soloud * cl = (Soloud *)aClassPtr;
+ cl->mixSigned16(aBuffer, aSamples);
+}
+
+void AudioAttenuator_destroy(void * aClassPtr)
+{
+ delete (AudioAttenuator *)aClassPtr;
+}
+
+float AudioAttenuator_attenuate(void * aClassPtr, float aDistance, float aMinDistance, float aMaxDistance, float aRolloffFactor)
+{
+ AudioAttenuator * cl = (AudioAttenuator *)aClassPtr;
+ return cl->attenuate(aDistance, aMinDistance, aMaxDistance, aRolloffFactor);
+}
+
+void BassboostFilter_destroy(void * aClassPtr)
+{
+ delete (BassboostFilter *)aClassPtr;
+}
+
+int BassboostFilter_setParams(void * aClassPtr, float aBoost)
+{
+ BassboostFilter * cl = (BassboostFilter *)aClassPtr;
+ return cl->setParams(aBoost);
+}
+
+void * BassboostFilter_create()
+{
+ return (void *)new BassboostFilter;
+}
+
+void BiquadResonantFilter_destroy(void * aClassPtr)
+{
+ delete (BiquadResonantFilter *)aClassPtr;
+}
+
+void * BiquadResonantFilter_create()
+{
+ return (void *)new BiquadResonantFilter;
+}
+
+int BiquadResonantFilter_setParams(void * aClassPtr, int aType, float aSampleRate, float aFrequency, float aResonance)
+{
+ BiquadResonantFilter * cl = (BiquadResonantFilter *)aClassPtr;
+ return cl->setParams(aType, aSampleRate, aFrequency, aResonance);
+}
+
+void Bus_destroy(void * aClassPtr)
+{
+ delete (Bus *)aClassPtr;
+}
+
+void * Bus_create()
+{
+ return (void *)new Bus;
+}
+
+void Bus_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+unsigned int Bus_play(void * aClassPtr, AudioSource * aSound)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->play(*aSound);
+}
+
+unsigned int Bus_playEx(void * aClassPtr, AudioSource * aSound, float aVolume, float aPan, int aPaused)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->play(*aSound, aVolume, aPan, !!aPaused);
+}
+
+unsigned int Bus_playClocked(void * aClassPtr, double aSoundTime, AudioSource * aSound)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->playClocked(aSoundTime, *aSound);
+}
+
+unsigned int Bus_playClockedEx(void * aClassPtr, double aSoundTime, AudioSource * aSound, float aVolume, float aPan)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->playClocked(aSoundTime, *aSound, aVolume, aPan);
+}
+
+unsigned int Bus_play3d(void * aClassPtr, AudioSource * aSound, float aPosX, float aPosY, float aPosZ)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->play3d(*aSound, aPosX, aPosY, aPosZ);
+}
+
+unsigned int Bus_play3dEx(void * aClassPtr, AudioSource * aSound, float aPosX, float aPosY, float aPosZ, float aVelX, float aVelY, float aVelZ, float aVolume, int aPaused)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->play3d(*aSound, aPosX, aPosY, aPosZ, aVelX, aVelY, aVelZ, aVolume, !!aPaused);
+}
+
+unsigned int Bus_play3dClocked(void * aClassPtr, double aSoundTime, AudioSource * aSound, float aPosX, float aPosY, float aPosZ)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->play3dClocked(aSoundTime, *aSound, aPosX, aPosY, aPosZ);
+}
+
+unsigned int Bus_play3dClockedEx(void * aClassPtr, double aSoundTime, AudioSource * aSound, float aPosX, float aPosY, float aPosZ, float aVelX, float aVelY, float aVelZ, float aVolume)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->play3dClocked(aSoundTime, *aSound, aPosX, aPosY, aPosZ, aVelX, aVelY, aVelZ, aVolume);
+}
+
+int Bus_setChannels(void * aClassPtr, unsigned int aChannels)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->setChannels(aChannels);
+}
+
+void Bus_setVisualizationEnable(void * aClassPtr, int aEnable)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->setVisualizationEnable(!!aEnable);
+}
+
+float * Bus_calcFFT(void * aClassPtr)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->calcFFT();
+}
+
+float * Bus_getWave(void * aClassPtr)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->getWave();
+}
+
+float Bus_getApproximateVolume(void * aClassPtr, unsigned int aChannel)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->getApproximateVolume(aChannel);
+}
+
+void Bus_setVolume(void * aClassPtr, float aVolume)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Bus_setLooping(void * aClassPtr, int aLoop)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Bus_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Bus_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Bus_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Bus_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Bus_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Bus_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Bus_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Bus_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Bus_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Bus_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Bus_getLoopPoint(void * aClassPtr)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Bus_stop(void * aClassPtr)
+{
+ Bus * cl = (Bus *)aClassPtr;
+ cl->stop();
+}
+
+void DCRemovalFilter_destroy(void * aClassPtr)
+{
+ delete (DCRemovalFilter *)aClassPtr;
+}
+
+void * DCRemovalFilter_create()
+{
+ return (void *)new DCRemovalFilter;
+}
+
+int DCRemovalFilter_setParams(void * aClassPtr)
+{
+ DCRemovalFilter * cl = (DCRemovalFilter *)aClassPtr;
+ return cl->setParams();
+}
+
+int DCRemovalFilter_setParamsEx(void * aClassPtr, float aLength)
+{
+ DCRemovalFilter * cl = (DCRemovalFilter *)aClassPtr;
+ return cl->setParams(aLength);
+}
+
+void EchoFilter_destroy(void * aClassPtr)
+{
+ delete (EchoFilter *)aClassPtr;
+}
+
+void * EchoFilter_create()
+{
+ return (void *)new EchoFilter;
+}
+
+int EchoFilter_setParams(void * aClassPtr, float aDelay)
+{
+ EchoFilter * cl = (EchoFilter *)aClassPtr;
+ return cl->setParams(aDelay);
+}
+
+int EchoFilter_setParamsEx(void * aClassPtr, float aDelay, float aDecay, float aFilter)
+{
+ EchoFilter * cl = (EchoFilter *)aClassPtr;
+ return cl->setParams(aDelay, aDecay, aFilter);
+}
+
+void FFTFilter_destroy(void * aClassPtr)
+{
+ delete (FFTFilter *)aClassPtr;
+}
+
+void * FFTFilter_create()
+{
+ return (void *)new FFTFilter;
+}
+
+void FlangerFilter_destroy(void * aClassPtr)
+{
+ delete (FlangerFilter *)aClassPtr;
+}
+
+void * FlangerFilter_create()
+{
+ return (void *)new FlangerFilter;
+}
+
+int FlangerFilter_setParams(void * aClassPtr, float aDelay, float aFreq)
+{
+ FlangerFilter * cl = (FlangerFilter *)aClassPtr;
+ return cl->setParams(aDelay, aFreq);
+}
+
+void LofiFilter_destroy(void * aClassPtr)
+{
+ delete (LofiFilter *)aClassPtr;
+}
+
+void * LofiFilter_create()
+{
+ return (void *)new LofiFilter;
+}
+
+int LofiFilter_setParams(void * aClassPtr, float aSampleRate, float aBitdepth)
+{
+ LofiFilter * cl = (LofiFilter *)aClassPtr;
+ return cl->setParams(aSampleRate, aBitdepth);
+}
+
+void Monotone_destroy(void * aClassPtr)
+{
+ delete (Monotone *)aClassPtr;
+}
+
+void * Monotone_create()
+{
+ return (void *)new Monotone;
+}
+
+int Monotone_setParams(void * aClassPtr, int aHardwareChannels)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ return cl->setParams(aHardwareChannels);
+}
+
+int Monotone_setParamsEx(void * aClassPtr, int aHardwareChannels, int aWaveform)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ return cl->setParams(aHardwareChannels, aWaveform);
+}
+
+int Monotone_load(void * aClassPtr, const char * aFilename)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ return cl->load(aFilename);
+}
+
+int Monotone_loadMem(void * aClassPtr, unsigned char * aMem, unsigned int aLength)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ return cl->loadMem(aMem, aLength);
+}
+
+int Monotone_loadMemEx(void * aClassPtr, unsigned char * aMem, unsigned int aLength, int aCopy, int aTakeOwnership)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ return cl->loadMem(aMem, aLength, !!aCopy, !!aTakeOwnership);
+}
+
+int Monotone_loadFile(void * aClassPtr, File * aFile)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ return cl->loadFile(aFile);
+}
+
+void Monotone_setVolume(void * aClassPtr, float aVolume)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Monotone_setLooping(void * aClassPtr, int aLoop)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Monotone_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Monotone_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Monotone_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Monotone_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Monotone_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Monotone_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Monotone_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Monotone_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Monotone_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Monotone_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Monotone_getLoopPoint(void * aClassPtr)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Monotone_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void Monotone_stop(void * aClassPtr)
+{
+ Monotone * cl = (Monotone *)aClassPtr;
+ cl->stop();
+}
+
+void Openmpt_destroy(void * aClassPtr)
+{
+ delete (Openmpt *)aClassPtr;
+}
+
+void * Openmpt_create()
+{
+ return (void *)new Openmpt;
+}
+
+int Openmpt_load(void * aClassPtr, const char * aFilename)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ return cl->load(aFilename);
+}
+
+int Openmpt_loadMem(void * aClassPtr, unsigned char * aMem, unsigned int aLength)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ return cl->loadMem(aMem, aLength);
+}
+
+int Openmpt_loadMemEx(void * aClassPtr, unsigned char * aMem, unsigned int aLength, int aCopy, int aTakeOwnership)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ return cl->loadMem(aMem, aLength, !!aCopy, !!aTakeOwnership);
+}
+
+int Openmpt_loadFile(void * aClassPtr, File * aFile)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ return cl->loadFile(aFile);
+}
+
+void Openmpt_setVolume(void * aClassPtr, float aVolume)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Openmpt_setLooping(void * aClassPtr, int aLoop)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Openmpt_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Openmpt_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Openmpt_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Openmpt_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Openmpt_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Openmpt_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Openmpt_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Openmpt_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Openmpt_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Openmpt_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Openmpt_getLoopPoint(void * aClassPtr)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Openmpt_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void Openmpt_stop(void * aClassPtr)
+{
+ Openmpt * cl = (Openmpt *)aClassPtr;
+ cl->stop();
+}
+
+void Queue_destroy(void * aClassPtr)
+{
+ delete (Queue *)aClassPtr;
+}
+
+void * Queue_create()
+{
+ return (void *)new Queue;
+}
+
+int Queue_play(void * aClassPtr, AudioSource * aSound)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ return cl->play(*aSound);
+}
+
+unsigned int Queue_getQueueCount(void * aClassPtr)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ return cl->getQueueCount();
+}
+
+int Queue_isCurrentlyPlaying(void * aClassPtr, AudioSource * aSound)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ return cl->isCurrentlyPlaying(*aSound);
+}
+
+int Queue_setParamsFromAudioSource(void * aClassPtr, AudioSource * aSound)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ return cl->setParamsFromAudioSource(*aSound);
+}
+
+int Queue_setParams(void * aClassPtr, float aSamplerate)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ return cl->setParams(aSamplerate);
+}
+
+int Queue_setParamsEx(void * aClassPtr, float aSamplerate, unsigned int aChannels)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ return cl->setParams(aSamplerate, aChannels);
+}
+
+void Queue_setVolume(void * aClassPtr, float aVolume)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Queue_setLooping(void * aClassPtr, int aLoop)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Queue_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Queue_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Queue_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Queue_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Queue_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Queue_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Queue_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Queue_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Queue_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Queue_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Queue_getLoopPoint(void * aClassPtr)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Queue_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void Queue_stop(void * aClassPtr)
+{
+ Queue * cl = (Queue *)aClassPtr;
+ cl->stop();
+}
+
+void RobotizeFilter_destroy(void * aClassPtr)
+{
+ delete (RobotizeFilter *)aClassPtr;
+}
+
+void * RobotizeFilter_create()
+{
+ return (void *)new RobotizeFilter;
+}
+
+void Prg_destroy(void * aClassPtr)
+{
+ delete (Prg *)aClassPtr;
+}
+
+void * Prg_create()
+{
+ return (void *)new Prg;
+}
+
+unsigned int Prg_rand(void * aClassPtr)
+{
+ Prg * cl = (Prg *)aClassPtr;
+ return cl->rand();
+}
+
+void Prg_srand(void * aClassPtr, int aSeed)
+{
+ Prg * cl = (Prg *)aClassPtr;
+ cl->srand(aSeed);
+}
+
+void Sfxr_destroy(void * aClassPtr)
+{
+ delete (Sfxr *)aClassPtr;
+}
+
+void * Sfxr_create()
+{
+ return (void *)new Sfxr;
+}
+
+void Sfxr_resetParams(void * aClassPtr)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->resetParams();
+}
+
+int Sfxr_loadParams(void * aClassPtr, const char * aFilename)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ return cl->loadParams(aFilename);
+}
+
+int Sfxr_loadParamsMem(void * aClassPtr, unsigned char * aMem, unsigned int aLength)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ return cl->loadParamsMem(aMem, aLength);
+}
+
+int Sfxr_loadParamsMemEx(void * aClassPtr, unsigned char * aMem, unsigned int aLength, int aCopy, int aTakeOwnership)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ return cl->loadParamsMem(aMem, aLength, !!aCopy, !!aTakeOwnership);
+}
+
+int Sfxr_loadParamsFile(void * aClassPtr, File * aFile)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ return cl->loadParamsFile(aFile);
+}
+
+int Sfxr_loadPreset(void * aClassPtr, int aPresetNo, int aRandSeed)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ return cl->loadPreset(aPresetNo, aRandSeed);
+}
+
+void Sfxr_setVolume(void * aClassPtr, float aVolume)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Sfxr_setLooping(void * aClassPtr, int aLoop)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Sfxr_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Sfxr_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Sfxr_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Sfxr_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Sfxr_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Sfxr_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Sfxr_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Sfxr_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Sfxr_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Sfxr_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Sfxr_getLoopPoint(void * aClassPtr)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Sfxr_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void Sfxr_stop(void * aClassPtr)
+{
+ Sfxr * cl = (Sfxr *)aClassPtr;
+ cl->stop();
+}
+
+void Speech_destroy(void * aClassPtr)
+{
+ delete (Speech *)aClassPtr;
+}
+
+void * Speech_create()
+{
+ return (void *)new Speech;
+}
+
+int Speech_setText(void * aClassPtr, const char * aText)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ return cl->setText(aText);
+}
+
+int Speech_setParams(void * aClassPtr)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ return cl->setParams();
+}
+
+int Speech_setParamsEx(void * aClassPtr, unsigned int aBaseFrequency, float aBaseSpeed, float aBaseDeclination, int aBaseWaveform)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ return cl->setParams(aBaseFrequency, aBaseSpeed, aBaseDeclination, aBaseWaveform);
+}
+
+void Speech_setVolume(void * aClassPtr, float aVolume)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Speech_setLooping(void * aClassPtr, int aLoop)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Speech_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Speech_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Speech_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Speech_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Speech_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Speech_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Speech_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Speech_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Speech_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Speech_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Speech_getLoopPoint(void * aClassPtr)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Speech_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void Speech_stop(void * aClassPtr)
+{
+ Speech * cl = (Speech *)aClassPtr;
+ cl->stop();
+}
+
+void TedSid_destroy(void * aClassPtr)
+{
+ delete (TedSid *)aClassPtr;
+}
+
+void * TedSid_create()
+{
+ return (void *)new TedSid;
+}
+
+int TedSid_load(void * aClassPtr, const char * aFilename)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ return cl->load(aFilename);
+}
+
+int TedSid_loadToMem(void * aClassPtr, const char * aFilename)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ return cl->loadToMem(aFilename);
+}
+
+int TedSid_loadMem(void * aClassPtr, unsigned char * aMem, unsigned int aLength)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ return cl->loadMem(aMem, aLength);
+}
+
+int TedSid_loadMemEx(void * aClassPtr, unsigned char * aMem, unsigned int aLength, int aCopy, int aTakeOwnership)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ return cl->loadMem(aMem, aLength, !!aCopy, !!aTakeOwnership);
+}
+
+int TedSid_loadFileToMem(void * aClassPtr, File * aFile)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ return cl->loadFileToMem(aFile);
+}
+
+int TedSid_loadFile(void * aClassPtr, File * aFile)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ return cl->loadFile(aFile);
+}
+
+void TedSid_setVolume(void * aClassPtr, float aVolume)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void TedSid_setLooping(void * aClassPtr, int aLoop)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void TedSid_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void TedSid_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void TedSid_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void TedSid_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void TedSid_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void TedSid_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void TedSid_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void TedSid_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void TedSid_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void TedSid_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double TedSid_getLoopPoint(void * aClassPtr)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void TedSid_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void TedSid_stop(void * aClassPtr)
+{
+ TedSid * cl = (TedSid *)aClassPtr;
+ cl->stop();
+}
+
+void Vic_destroy(void * aClassPtr)
+{
+ delete (Vic *)aClassPtr;
+}
+
+void * Vic_create()
+{
+ return (void *)new Vic;
+}
+
+void Vic_setModel(void * aClassPtr, int model)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->setModel(model);
+}
+
+int Vic_getModel(void * aClassPtr)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ return cl->getModel();
+}
+
+void Vic_setRegister(void * aClassPtr, int reg, unsigned char value)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->setRegister(reg, value);
+}
+
+unsigned char Vic_getRegister(void * aClassPtr, int reg)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ return cl->getRegister(reg);
+}
+
+void Vic_setVolume(void * aClassPtr, float aVolume)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Vic_setLooping(void * aClassPtr, int aLoop)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Vic_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Vic_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Vic_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Vic_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Vic_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Vic_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Vic_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Vic_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Vic_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Vic_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Vic_getLoopPoint(void * aClassPtr)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Vic_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void Vic_stop(void * aClassPtr)
+{
+ Vic * cl = (Vic *)aClassPtr;
+ cl->stop();
+}
+
+void Vizsn_destroy(void * aClassPtr)
+{
+ delete (Vizsn *)aClassPtr;
+}
+
+void * Vizsn_create()
+{
+ return (void *)new Vizsn;
+}
+
+void Vizsn_setText(void * aClassPtr, char * aText)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->setText(aText);
+}
+
+void Vizsn_setVolume(void * aClassPtr, float aVolume)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Vizsn_setLooping(void * aClassPtr, int aLoop)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Vizsn_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Vizsn_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Vizsn_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Vizsn_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Vizsn_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Vizsn_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Vizsn_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Vizsn_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Vizsn_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Vizsn_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Vizsn_getLoopPoint(void * aClassPtr)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Vizsn_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void Vizsn_stop(void * aClassPtr)
+{
+ Vizsn * cl = (Vizsn *)aClassPtr;
+ cl->stop();
+}
+
+void Wav_destroy(void * aClassPtr)
+{
+ delete (Wav *)aClassPtr;
+}
+
+void * Wav_create()
+{
+ return (void *)new Wav;
+}
+
+int Wav_load(void * aClassPtr, const char * aFilename)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->load(aFilename);
+}
+
+int Wav_loadMem(void * aClassPtr, unsigned char * aMem, unsigned int aLength)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadMem(aMem, aLength);
+}
+
+int Wav_loadMemEx(void * aClassPtr, unsigned char * aMem, unsigned int aLength, int aCopy, int aTakeOwnership)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadMem(aMem, aLength, !!aCopy, !!aTakeOwnership);
+}
+
+int Wav_loadFile(void * aClassPtr, File * aFile)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadFile(aFile);
+}
+
+int Wav_loadRawWave8(void * aClassPtr, unsigned char * aMem, unsigned int aLength)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadRawWave8(aMem, aLength);
+}
+
+int Wav_loadRawWave8Ex(void * aClassPtr, unsigned char * aMem, unsigned int aLength, float aSamplerate, unsigned int aChannels)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadRawWave8(aMem, aLength, aSamplerate, aChannels);
+}
+
+int Wav_loadRawWave16(void * aClassPtr, short * aMem, unsigned int aLength)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadRawWave16(aMem, aLength);
+}
+
+int Wav_loadRawWave16Ex(void * aClassPtr, short * aMem, unsigned int aLength, float aSamplerate, unsigned int aChannels)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadRawWave16(aMem, aLength, aSamplerate, aChannels);
+}
+
+int Wav_loadRawWave(void * aClassPtr, float * aMem, unsigned int aLength)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadRawWave(aMem, aLength);
+}
+
+int Wav_loadRawWaveEx(void * aClassPtr, float * aMem, unsigned int aLength, float aSamplerate, unsigned int aChannels, int aCopy, int aTakeOwnership)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->loadRawWave(aMem, aLength, aSamplerate, aChannels, !!aCopy, !!aTakeOwnership);
+}
+
+double Wav_getLength(void * aClassPtr)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->getLength();
+}
+
+void Wav_setVolume(void * aClassPtr, float aVolume)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void Wav_setLooping(void * aClassPtr, int aLoop)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void Wav_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void Wav_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void Wav_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void Wav_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void Wav_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void Wav_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void Wav_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void Wav_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void Wav_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void Wav_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double Wav_getLoopPoint(void * aClassPtr)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void Wav_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void Wav_stop(void * aClassPtr)
+{
+ Wav * cl = (Wav *)aClassPtr;
+ cl->stop();
+}
+
+void WaveShaperFilter_destroy(void * aClassPtr)
+{
+ delete (WaveShaperFilter *)aClassPtr;
+}
+
+int WaveShaperFilter_setParams(void * aClassPtr, float aAmount)
+{
+ WaveShaperFilter * cl = (WaveShaperFilter *)aClassPtr;
+ return cl->setParams(aAmount);
+}
+
+int WaveShaperFilter_setParamsEx(void * aClassPtr, float aAmount, float aWet)
+{
+ WaveShaperFilter * cl = (WaveShaperFilter *)aClassPtr;
+ return cl->setParams(aAmount, aWet);
+}
+
+void * WaveShaperFilter_create()
+{
+ return (void *)new WaveShaperFilter;
+}
+
+void WavStream_destroy(void * aClassPtr)
+{
+ delete (WavStream *)aClassPtr;
+}
+
+void * WavStream_create()
+{
+ return (void *)new WavStream;
+}
+
+int WavStream_load(void * aClassPtr, const char * aFilename)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ return cl->load(aFilename);
+}
+
+int WavStream_loadMem(void * aClassPtr, unsigned char * aData, unsigned int aDataLen)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ return cl->loadMem(aData, aDataLen);
+}
+
+int WavStream_loadMemEx(void * aClassPtr, unsigned char * aData, unsigned int aDataLen, int aCopy, int aTakeOwnership)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ return cl->loadMem(aData, aDataLen, !!aCopy, !!aTakeOwnership);
+}
+
+int WavStream_loadToMem(void * aClassPtr, const char * aFilename)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ return cl->loadToMem(aFilename);
+}
+
+int WavStream_loadFile(void * aClassPtr, File * aFile)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ return cl->loadFile(aFile);
+}
+
+int WavStream_loadFileToMem(void * aClassPtr, File * aFile)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ return cl->loadFileToMem(aFile);
+}
+
+double WavStream_getLength(void * aClassPtr)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ return cl->getLength();
+}
+
+void WavStream_setVolume(void * aClassPtr, float aVolume)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->setVolume(aVolume);
+}
+
+void WavStream_setLooping(void * aClassPtr, int aLoop)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->setLooping(!!aLoop);
+}
+
+void WavStream_set3dMinMaxDistance(void * aClassPtr, float aMinDistance, float aMaxDistance)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->set3dMinMaxDistance(aMinDistance, aMaxDistance);
+}
+
+void WavStream_set3dAttenuation(void * aClassPtr, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->set3dAttenuation(aAttenuationModel, aAttenuationRolloffFactor);
+}
+
+void WavStream_set3dDopplerFactor(void * aClassPtr, float aDopplerFactor)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->set3dDopplerFactor(aDopplerFactor);
+}
+
+void WavStream_set3dListenerRelative(void * aClassPtr, int aListenerRelative)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->set3dListenerRelative(!!aListenerRelative);
+}
+
+void WavStream_set3dDistanceDelay(void * aClassPtr, int aDistanceDelay)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->set3dDistanceDelay(!!aDistanceDelay);
+}
+
+void WavStream_set3dCollider(void * aClassPtr, AudioCollider * aCollider)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->set3dCollider(aCollider);
+}
+
+void WavStream_set3dColliderEx(void * aClassPtr, AudioCollider * aCollider, int aUserData)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->set3dCollider(aCollider, aUserData);
+}
+
+void WavStream_set3dAttenuator(void * aClassPtr, AudioAttenuator * aAttenuator)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->set3dAttenuator(aAttenuator);
+}
+
+void WavStream_setInaudibleBehavior(void * aClassPtr, int aMustTick, int aKill)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->setInaudibleBehavior(!!aMustTick, !!aKill);
+}
+
+void WavStream_setLoopPoint(void * aClassPtr, double aLoopPoint)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->setLoopPoint(aLoopPoint);
+}
+
+double WavStream_getLoopPoint(void * aClassPtr)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ return cl->getLoopPoint();
+}
+
+void WavStream_setFilter(void * aClassPtr, unsigned int aFilterId, Filter * aFilter)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->setFilter(aFilterId, aFilter);
+}
+
+void WavStream_stop(void * aClassPtr)
+{
+ WavStream * cl = (WavStream *)aClassPtr;
+ cl->stop();
+}
+
+} // extern "C"
+
diff --git a/src/soloud/src/core/soloud.cpp b/src/soloud/src/core/soloud.cpp
new file mode 100644
index 0000000..8406368
--- /dev/null
+++ b/src/soloud/src/core/soloud.cpp
@@ -0,0 +1,2005 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include // sin
+#include "soloud_internal.h"
+#include "soloud_thread.h"
+#include "soloud_fft.h"
+
+#ifdef SOLOUD_SSE_INTRINSICS
+#include
+#endif
+
+//#define FLOATING_POINT_DEBUG
+
+#ifdef FLOATING_POINT_DEBUG
+#include
+#endif
+
+
+
+#if !defined(WITH_SDL2) && !defined(WITH_SDL1) && !defined(WITH_PORTAUDIO) && \
+ !defined(WITH_OPENAL) && !defined(WITH_XAUDIO2) && !defined(WITH_WINMM) && \
+ !defined(WITH_WASAPI) && !defined(WITH_OSS) && !defined(WITH_SDL1_STATIC) && \
+ !defined(WITH_SDL2_STATIC) && !defined(WITH_ALSA) && !defined(WITH_OPENSLES) && \
+ !defined(WITH_NULL) && !defined(WITH_COREAUDIO) && !defined(WITH_VITA_HOMEBREW)
+#error It appears you haven't enabled any of the back-ends. Please #define one or more of the WITH_ defines (or use premake) '
+#endif
+
+
+namespace SoLoud
+{
+ AlignedFloatBuffer::AlignedFloatBuffer()
+ {
+ mBasePtr = 0;
+ mData = 0;
+ }
+
+ result AlignedFloatBuffer::init(unsigned int aFloats)
+ {
+ delete[] mBasePtr;
+ mBasePtr = 0;
+ mData = 0;
+ mFloats = aFloats;
+#ifdef DISABLE_SIMD
+ mBasePtr = new unsigned char[aFloats * sizeof(float)];
+ if (mBasePtr == NULL)
+ return OUT_OF_MEMORY;
+ mData = mBasePtr;
+#else
+ mBasePtr = new unsigned char[aFloats * sizeof(float) + 16];
+ if (mBasePtr == NULL)
+ return OUT_OF_MEMORY;
+ mData = (float *)(((size_t)mBasePtr + 15)&~15);
+#endif
+ return SO_NO_ERROR;
+ }
+
+ void AlignedFloatBuffer::clear()
+ {
+ memset(mData, 0, sizeof(float) * mFloats);
+ }
+
+ AlignedFloatBuffer::~AlignedFloatBuffer()
+ {
+ delete[] mBasePtr;
+ }
+
+ TinyAlignedFloatBuffer::TinyAlignedFloatBuffer()
+ {
+ unsigned char * basePtr = &mActualData[0];
+ mData = (float *)(((size_t)basePtr + 15)&~15);
+ }
+
+ Soloud::Soloud()
+ {
+#ifdef FLOATING_POINT_DEBUG
+ unsigned int u;
+ u = _controlfp(0, 0);
+ u = u & ~(_EM_INVALID | /*_EM_DENORMAL |*/ _EM_ZERODIVIDE | _EM_OVERFLOW /*| _EM_UNDERFLOW | _EM_INEXACT*/);
+ _controlfp(u, _MCW_EM);
+#endif
+ mInsideAudioThreadMutex = false;
+ mScratchSize = 0;
+ mScratchNeeded = 0;
+ mSamplerate = 0;
+ mBufferSize = 0;
+ mFlags = 0;
+ mGlobalVolume = 0;
+ mPlayIndex = 0;
+ mBackendData = NULL;
+ mAudioThreadMutex = NULL;
+ mPostClipScaler = 0;
+ mBackendCleanupFunc = NULL;
+ mChannels = 2;
+ mStreamTime = 0;
+ mLastClockedTime = 0;
+ mAudioSourceID = 1;
+ mBackendString = 0;
+ mBackendID = 0;
+ mActiveVoiceDirty = true;
+ mActiveVoiceCount = 0;
+ int i;
+ for (i = 0; i < VOICE_COUNT; i++)
+ mActiveVoice[i] = 0;
+ for (i = 0; i < FILTERS_PER_STREAM; i++)
+ {
+ mFilter[i] = NULL;
+ mFilterInstance[i] = NULL;
+ }
+ for (i = 0; i < 256; i++)
+ {
+ mFFTData[i] = 0;
+ mVisualizationWaveData[i] = 0;
+ mWaveData[i] = 0;
+ }
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ mVisualizationChannelVolume[i] = 0;
+ }
+ for (i = 0; i < VOICE_COUNT; i++)
+ {
+ mVoice[i] = 0;
+ }
+ mVoiceGroup = 0;
+ mVoiceGroupCount = 0;
+
+ m3dPosition[0] = 0;
+ m3dPosition[1] = 0;
+ m3dPosition[2] = 0;
+ m3dAt[0] = 0;
+ m3dAt[1] = 0;
+ m3dAt[2] = -1;
+ m3dUp[0] = 0;
+ m3dUp[1] = 1;
+ m3dUp[2] = 0;
+ m3dVelocity[0] = 0;
+ m3dVelocity[1] = 0;
+ m3dVelocity[2] = 0;
+ m3dSoundSpeed = 343.3f;
+ mMaxActiveVoices = 16;
+ mHighestVoice = 0;
+ mActiveVoiceDirty = true;
+ mResampleData = NULL;
+ }
+
+ Soloud::~Soloud()
+ {
+ // let's stop all sounds before deinit, so we don't mess up our mutexes
+ stopAll();
+ deinit();
+ unsigned int i;
+ for (i = 0; i < FILTERS_PER_STREAM; i++)
+ {
+ delete mFilterInstance[i];
+ }
+ for (i = 0; i < mVoiceGroupCount; i++)
+ delete[] mVoiceGroup[i];
+ delete[] mVoiceGroup;
+ delete[] mResampleData;
+ }
+
+ void Soloud::deinit()
+ {
+ SOLOUD_ASSERT(!mInsideAudioThreadMutex);
+ if (mBackendCleanupFunc)
+ mBackendCleanupFunc(this);
+ mBackendCleanupFunc = 0;
+ if (mAudioThreadMutex)
+ Thread::destroyMutex(mAudioThreadMutex);
+ mAudioThreadMutex = NULL;
+ }
+
+ result Soloud::init(unsigned int aFlags, unsigned int aBackend, unsigned int aSamplerate, unsigned int aBufferSize, unsigned int aChannels)
+ {
+ if (aBackend >= BACKEND_MAX || aChannels == 3 || aChannels == 5 || aChannels == 7 || aChannels > MAX_CHANNELS)
+ return INVALID_PARAMETER;
+
+ deinit();
+
+ mAudioThreadMutex = Thread::createMutex();
+
+ mBackendID = 0;
+ mBackendString = 0;
+
+ int samplerate = 44100;
+ int buffersize = 2048;
+ int inited = 0;
+
+ if (aSamplerate != Soloud::AUTO) samplerate = aSamplerate;
+ if (aBufferSize != Soloud::AUTO) buffersize = aBufferSize;
+
+#if defined(WITH_SDL1_STATIC)
+ if (!inited &&
+ (aBackend == Soloud::SDL1 ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = sdl1static_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::SDL1;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_SDL2_STATIC)
+ if (!inited &&
+ (aBackend == Soloud::SDL2 ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = sdl2static_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::SDL2;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_SDL2)
+ if (!inited &&
+ (aBackend == Soloud::SDL2 ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = sdl2_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::SDL2;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_SDL1)
+ if (!inited &&
+ (aBackend == Soloud::SDL1 ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = sdl1_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::SDL1;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_PORTAUDIO)
+ if (!inited &&
+ (aBackend == Soloud::PORTAUDIO ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = portaudio_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::PORTAUDIO;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_XAUDIO2)
+ if (!inited &&
+ (aBackend == Soloud::XAUDIO2 ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 4096;
+
+ int ret = xaudio2_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::XAUDIO2;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_WINMM)
+ if (!inited &&
+ (aBackend == Soloud::WINMM ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 4096;
+
+ int ret = winmm_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::WINMM;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_WASAPI)
+ if (!inited &&
+ (aBackend == Soloud::WASAPI ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 4096;
+ if (aSamplerate == Soloud::AUTO) samplerate = 48000;
+
+ int ret = wasapi_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::WASAPI;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_ALSA)
+ if (!inited &&
+ (aBackend == Soloud::ALSA ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = alsa_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::ALSA;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_OSS)
+ if (!inited &&
+ (aBackend == Soloud::OSS ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = oss_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::OSS;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_OPENAL)
+ if (!inited &&
+ (aBackend == Soloud::OPENAL ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 4096;
+
+ int ret = openal_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::OPENAL;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_COREAUDIO)
+ if (!inited &&
+ (aBackend == Soloud::COREAUDIO ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = coreaudio_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::COREAUDIO;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_OPENSLES)
+ if (!inited &&
+ (aBackend == Soloud::OPENSLES ||
+ aBackend == Soloud::AUTO))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 4096;
+
+ int ret = opensles_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::OPENSLES;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_VITA_HOMEBREW)
+ if (!inited &&
+ (aBackend == Soloud::VITA_HOMEBREW ||
+ aBackend == Soloud::AUTO))
+ {
+ int ret = vita_homebrew_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::VITA_HOMEBREW;
+ }
+
+ if (ret != 0 && aBackend != Soloud::AUTO)
+ return ret;
+ }
+#endif
+
+#if defined(WITH_NULL)
+ if (!inited &&
+ (aBackend == Soloud::NULLDRIVER))
+ {
+ if (aBufferSize == Soloud::AUTO) buffersize = 2048;
+
+ int ret = null_init(this, aFlags, samplerate, buffersize, aChannels);
+ if (ret == 0)
+ {
+ inited = 1;
+ mBackendID = Soloud::NULLDRIVER;
+ }
+
+ if (ret != 0)
+ return ret;
+ }
+#endif
+
+ if (!inited && aBackend != Soloud::AUTO)
+ return NOT_IMPLEMENTED;
+ if (!inited)
+ return UNKNOWN_ERROR;
+ return 0;
+ }
+
+ void Soloud::postinit(unsigned int aSamplerate, unsigned int aBufferSize, unsigned int aFlags, unsigned int aChannels)
+ {
+ mGlobalVolume = 1;
+ mChannels = aChannels;
+ mSamplerate = aSamplerate;
+ mBufferSize = aBufferSize;
+ mScratchSize = aBufferSize;
+ if (mScratchSize < SAMPLE_GRANULARITY * 2) mScratchSize = SAMPLE_GRANULARITY * 2;
+ if (mScratchSize < 4096) mScratchSize = 4096;
+ mScratchNeeded = mScratchSize;
+ mScratch.init(mScratchSize * MAX_CHANNELS);
+ mOutputScratch.init(mScratchSize * MAX_CHANNELS);
+ mResampleData = new AlignedFloatBuffer[mMaxActiveVoices * 2];
+ mResampleDataOwner = new AudioSourceInstance*[mMaxActiveVoices];
+ unsigned int i;
+ for (i = 0; i < mMaxActiveVoices * 2; i++)
+ mResampleData[i].init(SAMPLE_GRANULARITY * MAX_CHANNELS);
+ for (i = 0; i < mMaxActiveVoices; i++)
+ mResampleDataOwner[i] = NULL;
+ mFlags = aFlags;
+ mPostClipScaler = 0.95f;
+ switch (mChannels)
+ {
+ case 1:
+ m3dSpeakerPosition[0 * 3 + 0] = 0;
+ m3dSpeakerPosition[0 * 3 + 1] = 0;
+ m3dSpeakerPosition[0 * 3 + 2] = 1;
+ break;
+ case 2:
+ m3dSpeakerPosition[0 * 3 + 0] = 2;
+ m3dSpeakerPosition[0 * 3 + 1] = 0;
+ m3dSpeakerPosition[0 * 3 + 2] = 1;
+ m3dSpeakerPosition[1 * 3 + 0] = -2;
+ m3dSpeakerPosition[1 * 3 + 1] = 0;
+ m3dSpeakerPosition[1 * 3 + 2] = 1;
+ break;
+ case 4:
+ m3dSpeakerPosition[0 * 3 + 0] = 2;
+ m3dSpeakerPosition[0 * 3 + 1] = 0;
+ m3dSpeakerPosition[0 * 3 + 2] = 1;
+ m3dSpeakerPosition[1 * 3 + 0] = -2;
+ m3dSpeakerPosition[1 * 3 + 1] = 0;
+ m3dSpeakerPosition[1 * 3 + 2] = 1;
+ // I suppose technically the second pair should be straight left & right,
+ // but I prefer moving them a bit back to mirror the front speakers.
+ m3dSpeakerPosition[2 * 3 + 0] = 2;
+ m3dSpeakerPosition[2 * 3 + 1] = 0;
+ m3dSpeakerPosition[2 * 3 + 2] = -1;
+ m3dSpeakerPosition[3 * 3 + 0] = -2;
+ m3dSpeakerPosition[3 * 3 + 1] = 0;
+ m3dSpeakerPosition[3 * 3 + 2] = -1;
+ break;
+ case 6:
+ m3dSpeakerPosition[0 * 3 + 0] = 2;
+ m3dSpeakerPosition[0 * 3 + 1] = 0;
+ m3dSpeakerPosition[0 * 3 + 2] = 1;
+ m3dSpeakerPosition[1 * 3 + 0] = -2;
+ m3dSpeakerPosition[1 * 3 + 1] = 0;
+ m3dSpeakerPosition[1 * 3 + 2] = 1;
+
+ // center and subwoofer.
+ m3dSpeakerPosition[2 * 3 + 0] = 0;
+ m3dSpeakerPosition[2 * 3 + 1] = 0;
+ m3dSpeakerPosition[2 * 3 + 2] = 1;
+ // Sub should be "mix of everything". We'll handle it as a special case and make it a null vector.
+ m3dSpeakerPosition[3 * 3 + 0] = 0;
+ m3dSpeakerPosition[3 * 3 + 1] = 0;
+ m3dSpeakerPosition[3 * 3 + 2] = 0;
+
+ // I suppose technically the second pair should be straight left & right,
+ // but I prefer moving them a bit back to mirror the front speakers.
+ m3dSpeakerPosition[4 * 3 + 0] = 2;
+ m3dSpeakerPosition[4 * 3 + 1] = 0;
+ m3dSpeakerPosition[4 * 3 + 2] = -1;
+ m3dSpeakerPosition[5 * 3 + 0] = -2;
+ m3dSpeakerPosition[5 * 3 + 1] = 0;
+ m3dSpeakerPosition[5 * 3 + 2] = -1;
+ break;
+ case 8:
+ m3dSpeakerPosition[0 * 3 + 0] = 2;
+ m3dSpeakerPosition[0 * 3 + 1] = 0;
+ m3dSpeakerPosition[0 * 3 + 2] = 1;
+ m3dSpeakerPosition[1 * 3 + 0] = -2;
+ m3dSpeakerPosition[1 * 3 + 1] = 0;
+ m3dSpeakerPosition[1 * 3 + 2] = 1;
+
+ // center and subwoofer.
+ m3dSpeakerPosition[2 * 3 + 0] = 0;
+ m3dSpeakerPosition[2 * 3 + 1] = 0;
+ m3dSpeakerPosition[2 * 3 + 2] = 1;
+ // Sub should be "mix of everything". We'll handle it as a special case and make it a null vector.
+ m3dSpeakerPosition[3 * 3 + 0] = 0;
+ m3dSpeakerPosition[3 * 3 + 1] = 0;
+ m3dSpeakerPosition[3 * 3 + 2] = 0;
+
+ // side
+ m3dSpeakerPosition[4 * 3 + 0] = 2;
+ m3dSpeakerPosition[4 * 3 + 1] = 0;
+ m3dSpeakerPosition[4 * 3 + 2] = 0;
+ m3dSpeakerPosition[5 * 3 + 0] = -2;
+ m3dSpeakerPosition[5 * 3 + 1] = 0;
+ m3dSpeakerPosition[5 * 3 + 2] = 0;
+
+ // back
+ m3dSpeakerPosition[6 * 3 + 0] = 2;
+ m3dSpeakerPosition[6 * 3 + 1] = 0;
+ m3dSpeakerPosition[6 * 3 + 2] = -1;
+ m3dSpeakerPosition[7 * 3 + 0] = -2;
+ m3dSpeakerPosition[7 * 3 + 1] = 0;
+ m3dSpeakerPosition[7 * 3 + 2] = -1;
+ break;
+ }
+ }
+
+ const char * Soloud::getErrorString(result aErrorCode) const
+ {
+ switch (aErrorCode)
+ {
+ case SO_NO_ERROR: return "No error";
+ case INVALID_PARAMETER: return "Some parameter is invalid";
+ case FILE_NOT_FOUND: return "File not found";
+ case FILE_LOAD_FAILED: return "File found, but could not be loaded";
+ case DLL_NOT_FOUND: return "DLL not found, or wrong DLL";
+ case OUT_OF_MEMORY: return "Out of memory";
+ case NOT_IMPLEMENTED: return "Feature not implemented";
+ /*case UNKNOWN_ERROR: return "Other error";*/
+ }
+ return "Other error";
+ }
+
+
+ float * Soloud::getWave()
+ {
+ int i;
+ lockAudioMutex();
+ for (i = 0; i < 256; i++)
+ mWaveData[i] = mVisualizationWaveData[i];
+ unlockAudioMutex();
+ return mWaveData;
+ }
+
+ float Soloud::getApproximateVolume(unsigned int aChannel)
+ {
+ if (aChannel > mChannels)
+ return 0;
+ float vol = 0;
+ lockAudioMutex();
+ vol = mVisualizationChannelVolume[aChannel];
+ unlockAudioMutex();
+ return vol;
+ }
+
+
+ float * Soloud::calcFFT()
+ {
+ lockAudioMutex();
+ float temp[1024];
+ int i;
+ for (i = 0; i < 256; i++)
+ {
+ temp[i*2] = mVisualizationWaveData[i];
+ temp[i*2+1] = 0;
+ temp[i+512] = 0;
+ temp[i+768] = 0;
+ }
+ unlockAudioMutex();
+
+ SoLoud::FFT::fft1024(temp);
+
+ for (i = 0; i < 256; i++)
+ {
+ float real = temp[i * 2];
+ float imag = temp[i * 2 + 1];
+ mFFTData[i] = (float)sqrt(real*real+imag*imag);
+ }
+
+ return mFFTData;
+ }
+
+#ifdef SOLOUD_SSE_INTRINSICS
+ void Soloud::clip(AlignedFloatBuffer &aBuffer, AlignedFloatBuffer &aDestBuffer, unsigned int aSamples, float aVolume0, float aVolume1)
+ {
+ float vd = (aVolume1 - aVolume0) / aSamples;
+ float v = aVolume0;
+ unsigned int i, j, c, d;
+ unsigned int samplequads = (aSamples + 3) / 4; // rounded up
+
+ // Clip
+ if (mFlags & CLIP_ROUNDOFF)
+ {
+ float nb = -1.65f; __m128 negbound = _mm_load_ps1(&nb);
+ float pb = 1.65f; __m128 posbound = _mm_load_ps1(&pb);
+ float ls = 0.87f; __m128 linearscale = _mm_load_ps1(&ls);
+ float cs = -0.1f; __m128 cubicscale = _mm_load_ps1(&cs);
+ float nw = -0.9862875f; __m128 negwall = _mm_load_ps1(&nw);
+ float pw = 0.9862875f; __m128 poswall = _mm_load_ps1(&pw);
+ __m128 postscale = _mm_load_ps1(&mPostClipScaler);
+ TinyAlignedFloatBuffer volumes;
+ volumes.mData[0] = v;
+ volumes.mData[1] = v + vd;
+ volumes.mData[2] = v + vd + vd;
+ volumes.mData[3] = v + vd + vd + vd;
+ vd *= 4;
+ __m128 vdelta = _mm_load_ps1(&vd);
+ c = 0;
+ d = 0;
+ for (j = 0; j < mChannels; j++)
+ {
+ __m128 vol = _mm_load_ps(volumes.mData);
+
+ for (i = 0; i < samplequads; i++)
+ {
+ //float f1 = origdata[c] * v; c++; v += vd;
+ __m128 f = _mm_load_ps(&aBuffer.mData[c]);
+ c += 4;
+ f = _mm_mul_ps(f, vol);
+ vol = _mm_add_ps(vol, vdelta);
+
+ //float u1 = (f1 > -1.65f);
+ __m128 u = _mm_cmpgt_ps(f, negbound);
+
+ //float o1 = (f1 < 1.65f);
+ __m128 o = _mm_cmplt_ps(f, posbound);
+
+ //f1 = (0.87f * f1 - 0.1f * f1 * f1 * f1) * u1 * o1;
+ __m128 lin = _mm_mul_ps(f, linearscale);
+ __m128 cubic = _mm_mul_ps(f, f);
+ cubic = _mm_mul_ps(cubic, f);
+ cubic = _mm_mul_ps(cubic, cubicscale);
+ f = _mm_add_ps(cubic, lin);
+
+ //f1 = f1 * u1 + !u1 * -0.9862875f;
+ __m128 lowmask = _mm_andnot_ps(u, negwall);
+ __m128 ilowmask = _mm_and_ps(u, f);
+ f = _mm_add_ps(lowmask, ilowmask);
+
+ //f1 = f1 * o1 + !o1 * 0.9862875f;
+ __m128 himask = _mm_andnot_ps(o, poswall);
+ __m128 ihimask = _mm_and_ps(o, f);
+ f = _mm_add_ps(himask, ihimask);
+
+ // outdata[d] = f1 * postclip; d++;
+ f = _mm_mul_ps(f, postscale);
+ _mm_store_ps(&aDestBuffer.mData[d], f);
+ d += 4;
+ }
+ }
+ }
+ else
+ {
+ float nb = -1.0f; __m128 negbound = _mm_load_ps1(&nb);
+ float pb = 1.0f; __m128 posbound = _mm_load_ps1(&pb);
+ __m128 postscale = _mm_load_ps1(&mPostClipScaler);
+ TinyAlignedFloatBuffer volumes;
+ volumes.mData[0] = v;
+ volumes.mData[1] = v + vd;
+ volumes.mData[2] = v + vd + vd;
+ volumes.mData[3] = v + vd + vd + vd;
+ vd *= 4;
+ __m128 vdelta = _mm_load_ps1(&vd);
+ c = 0;
+ d = 0;
+ for (j = 0; j < mChannels; j++)
+ {
+ __m128 vol = _mm_load_ps(volumes.mData);
+ for (i = 0; i < samplequads; i++)
+ {
+ //float f1 = aBuffer.mData[c] * v; c++; v += vd;
+ __m128 f = _mm_load_ps(&aBuffer.mData[c]);
+ c += 4;
+ f = _mm_mul_ps(f, vol);
+ vol = _mm_add_ps(vol, vdelta);
+
+ //f1 = (f1 <= -1) ? -1 : (f1 >= 1) ? 1 : f1;
+ f = _mm_max_ps(f, negbound);
+ f = _mm_min_ps(f, posbound);
+
+ //aDestBuffer.mData[d] = f1 * mPostClipScaler; d++;
+ f = _mm_mul_ps(f, postscale);
+ _mm_store_ps(&aDestBuffer.mData[d], f);
+ d += 4;
+ }
+ }
+ }
+ }
+#else // fallback code
+ void Soloud::clip(AlignedFloatBuffer &aBuffer, AlignedFloatBuffer &aDestBuffer, unsigned int aSamples, float aVolume0, float aVolume1)
+ {
+ float vd = (aVolume1 - aVolume0) / aSamples;
+ float v = aVolume0;
+ unsigned int i, j, c, d;
+ unsigned int samplequads = (aSamples + 3) / 4; // rounded up
+ // Clip
+ if (mFlags & CLIP_ROUNDOFF)
+ {
+ c = 0;
+ d = 0;
+ for (j = 0; j < mChannels; j++)
+ {
+ v = aVolume0;
+ for (i = 0; i < samplequads; i++)
+ {
+ float f1 = aBuffer.mData[c] * v; c++; v += vd;
+ float f2 = aBuffer.mData[c] * v; c++; v += vd;
+ float f3 = aBuffer.mData[c] * v; c++; v += vd;
+ float f4 = aBuffer.mData[c] * v; c++; v += vd;
+
+ f1 = (f1 <= -1.65f) ? -0.9862875f : (f1 >= 1.65f) ? 0.9862875f : (0.87f * f1 - 0.1f * f1 * f1 * f1);
+ f2 = (f2 <= -1.65f) ? -0.9862875f : (f2 >= 1.65f) ? 0.9862875f : (0.87f * f2 - 0.1f * f2 * f2 * f2);
+ f3 = (f3 <= -1.65f) ? -0.9862875f : (f3 >= 1.65f) ? 0.9862875f : (0.87f * f3 - 0.1f * f3 * f3 * f3);
+ f4 = (f4 <= -1.65f) ? -0.9862875f : (f4 >= 1.65f) ? 0.9862875f : (0.87f * f4 - 0.1f * f4 * f4 * f4);
+
+ aDestBuffer.mData[d] = f1 * mPostClipScaler; d++;
+ aDestBuffer.mData[d] = f2 * mPostClipScaler; d++;
+ aDestBuffer.mData[d] = f3 * mPostClipScaler; d++;
+ aDestBuffer.mData[d] = f4 * mPostClipScaler; d++;
+ }
+ }
+ }
+ else
+ {
+ c = 0;
+ d = 0;
+ for (j = 0; j < mChannels; j++)
+ {
+ v = aVolume0;
+ for (i = 0; i < samplequads; i++)
+ {
+ float f1 = aBuffer.mData[c] * v; c++; v += vd;
+ float f2 = aBuffer.mData[c] * v; c++; v += vd;
+ float f3 = aBuffer.mData[c] * v; c++; v += vd;
+ float f4 = aBuffer.mData[c] * v; c++; v += vd;
+
+ f1 = (f1 <= -1) ? -1 : (f1 >= 1) ? 1 : f1;
+ f2 = (f2 <= -1) ? -1 : (f2 >= 1) ? 1 : f2;
+ f3 = (f3 <= -1) ? -1 : (f3 >= 1) ? 1 : f3;
+ f4 = (f4 <= -1) ? -1 : (f4 >= 1) ? 1 : f4;
+
+ aDestBuffer.mData[d] = f1 * mPostClipScaler; d++;
+ aDestBuffer.mData[d] = f2 * mPostClipScaler; d++;
+ aDestBuffer.mData[d] = f3 * mPostClipScaler; d++;
+ aDestBuffer.mData[d] = f4 * mPostClipScaler; d++;
+ }
+ }
+ }
+}
+#endif
+
+#define FIXPOINT_FRAC_BITS 20
+#define FIXPOINT_FRAC_MUL (1 << FIXPOINT_FRAC_BITS)
+#define FIXPOINT_FRAC_MASK ((1 << FIXPOINT_FRAC_BITS) - 1)
+
+ void resample(float *aSrc,
+ float *aSrc1,
+ float *aDst,
+ int aSrcOffset,
+ int aDstSampleCount,
+ float aSrcSamplerate,
+ float aDstSamplerate,
+ int aStepFixed)
+ {
+#if 0
+
+#elif defined(RESAMPLER_LINEAR)
+ int i;
+ int pos = aSrcOffset;
+
+ for (i = 0; i < aDstSampleCount; i++, pos += aStepFixed)
+ {
+ int p = pos >> FIXPOINT_FRAC_BITS;
+ int f = pos & FIXPOINT_FRAC_MASK;
+#ifdef _DEBUG
+ if (p >= SAMPLE_GRANULARITY || p < 0)
+ {
+ // This should never actually happen
+ p = SAMPLE_GRANULARITY - 1;
+ }
+#endif
+ float s1 = aSrc1[SAMPLE_GRANULARITY - 1];
+ float s2 = aSrc[p];
+ if (p != 0)
+ {
+ s1 = aSrc[p-1];
+ }
+ aDst[i] = s1 + (s2 - s1) * f * (1 / (float)FIXPOINT_FRAC_MUL);
+ }
+#else // Point sample
+ int i;
+ int pos = aSrcOffset;
+
+ for (i = 0; i < aDstSampleCount; i++, pos += aStepFixed)
+ {
+ int p = pos >> FIXPOINT_FRAC_BITS;
+ aDst[i] = aSrc[p];
+ }
+#endif
+ }
+
+ void panAndExpand(AudioSourceInstance *aVoice, float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize, float *aScratch, unsigned int aChannels)
+ {
+ float pan[MAX_CHANNELS]; // current speaker volume
+ float pand[MAX_CHANNELS]; // destination speaker volume
+ float pani[MAX_CHANNELS]; // speaker volume increment per sample
+ unsigned int j, k;
+ for (k = 0; k < aChannels; k++)
+ {
+ pan[k] = aVoice->mCurrentChannelVolume[k];
+ pand[k] = aVoice->mChannelVolume[k] * aVoice->mOverallVolume;
+ pani[k] = (pand[k] - pan[k]) / aSamplesToRead; // TODO: this is a bit inconsistent.. but it's a hack to begin with
+ }
+
+ int ofs = 0;
+ switch (aChannels)
+ {
+ case 1: // Target is mono. Sum everything. (1->1, 2->1, 4->1, 6->1, 8->1)
+ for (j = 0, ofs = 0; j < aVoice->mChannels; j++, ofs += aBufferSize)
+ {
+ pan[0] = aVoice->mCurrentChannelVolume[0];
+ for (k = 0; k < aSamplesToRead; k++)
+ {
+ pan[0] += pani[0];
+ aBuffer[k] += aScratch[ofs + k] * pan[0];
+ }
+ }
+ break;
+ case 2:
+ switch (aVoice->mChannels)
+ {
+ case 8: // 8->2, just sum lefties and righties, add a bit of center and sub?
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ float s5 = aScratch[aBufferSize * 4 + j];
+ float s6 = aScratch[aBufferSize * 5 + j];
+ float s7 = aScratch[aBufferSize * 6 + j];
+ float s8 = aScratch[aBufferSize * 7 + j];
+ aBuffer[j + 0] += 0.2f * (s1 + s3 + s4 + s5 + s7) * pan[0];
+ aBuffer[j + aBufferSize] += 0.2f * (s2 + s3 + s4 + s6 + s8) * pan[1];
+ }
+ break;
+ case 6: // 6->2, just sum lefties and righties, add a bit of center and sub?
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ float s5 = aScratch[aBufferSize * 4 + j];
+ float s6 = aScratch[aBufferSize * 5 + j];
+ aBuffer[j + 0] += 0.3f * (s1 + s3 + s4 + s5) * pan[0];
+ aBuffer[j + aBufferSize] += 0.3f * (s2 + s3 + s4 + s6) * pan[1];
+ }
+ break;
+ case 4: // 4->2, just sum lefties and righties
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ aBuffer[j + 0] += 0.5f * (s1 + s3) * pan[0];
+ aBuffer[j + aBufferSize] += 0.5f * (s2 + s4) * pan[1];
+ }
+ break;
+ case 2: // 2->2
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ }
+ break;
+ case 1: // 1->2
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ float s = aScratch[j];
+ aBuffer[j + 0] += s * pan[0];
+ aBuffer[j + aBufferSize] += s * pan[1];
+ }
+ break;
+ }
+ break;
+ case 4:
+ switch (aVoice->mChannels)
+ {
+ case 8: // 8->4, add a bit of center, sub?
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ float s5 = aScratch[aBufferSize * 4 + j];
+ float s6 = aScratch[aBufferSize * 5 + j];
+ float s7 = aScratch[aBufferSize * 6 + j];
+ float s8 = aScratch[aBufferSize * 7 + j];
+ float c = (s3 + s4) * 0.7f;
+ aBuffer[j + 0] += s1 * pan[0] + c;
+ aBuffer[j + aBufferSize] += s2 * pan[1] + c;
+ aBuffer[j + aBufferSize * 2] += 0.5f * (s5 + s7) * pan[2];
+ aBuffer[j + aBufferSize * 3] += 0.5f * (s6 + s8) * pan[3];
+ }
+ break;
+ case 6: // 6->4, add a bit of center, sub?
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ float s5 = aScratch[aBufferSize * 4 + j];
+ float s6 = aScratch[aBufferSize * 5 + j];
+ float c = (s3 + s4) * 0.7f;
+ aBuffer[j + 0] += s1 * pan[0] + c;
+ aBuffer[j + aBufferSize] += s2 * pan[1] + c;
+ aBuffer[j + aBufferSize * 2] += s5 * pan[2];
+ aBuffer[j + aBufferSize * 3] += s6 * pan[3];
+ }
+ break;
+ case 4: // 4->4
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += s3 * pan[2];
+ aBuffer[j + aBufferSize * 3] += s4 * pan[3];
+ }
+ break;
+ case 2: // 2->4
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += s1 * pan[2];
+ aBuffer[j + aBufferSize * 3] += s2 * pan[3];
+ }
+ break;
+ case 1: // 1->4
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ float s = aScratch[j];
+ aBuffer[j + 0] += s * pan[0];
+ aBuffer[j + aBufferSize] += s * pan[1];
+ aBuffer[j + aBufferSize * 2] += s * pan[2];
+ aBuffer[j + aBufferSize * 3] += s * pan[3];
+ }
+ break;
+ }
+ break;
+ case 6:
+ switch (aVoice->mChannels)
+ {
+ case 8: // 8->6
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ float s5 = aScratch[aBufferSize * 4 + j];
+ float s6 = aScratch[aBufferSize * 5 + j];
+ float s7 = aScratch[aBufferSize * 6 + j];
+ float s8 = aScratch[aBufferSize * 7 + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += s3 * pan[2];
+ aBuffer[j + aBufferSize * 3] += s4 * pan[3];
+ aBuffer[j + aBufferSize * 4] += 0.5f * (s5 + s7) * pan[4];
+ aBuffer[j + aBufferSize * 5] += 0.5f * (s6 + s8) * pan[5];
+ }
+ break;
+ case 6: // 6->6
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ float s5 = aScratch[aBufferSize * 4 + j];
+ float s6 = aScratch[aBufferSize * 5 + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += s3 * pan[2];
+ aBuffer[j + aBufferSize * 3] += s4 * pan[3];
+ aBuffer[j + aBufferSize * 4] += s5 * pan[4];
+ aBuffer[j + aBufferSize * 5] += s6 * pan[5];
+ }
+ break;
+ case 4: // 4->6
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += 0.5f * (s1 + s2) * pan[2];
+ aBuffer[j + aBufferSize * 3] += 0.25f * (s1 + s2 + s3 + s4) * pan[3];
+ aBuffer[j + aBufferSize * 4] += s3 * pan[4];
+ aBuffer[j + aBufferSize * 5] += s4 * pan[5];
+ }
+ break;
+ case 2: // 2->6
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += 0.5f * (s1 + s2) * pan[2];
+ aBuffer[j + aBufferSize * 3] += 0.5f * (s1 + s2) * pan[3];
+ aBuffer[j + aBufferSize * 4] += s1 * pan[4];
+ aBuffer[j + aBufferSize * 5] += s2 * pan[5];
+ }
+ break;
+ case 1: // 1->6
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ float s = aScratch[j];
+ aBuffer[j + 0] += s * pan[0];
+ aBuffer[j + aBufferSize] += s * pan[1];
+ aBuffer[j + aBufferSize * 2] += s * pan[2];
+ aBuffer[j + aBufferSize * 3] += s * pan[3];
+ aBuffer[j + aBufferSize * 4] += s * pan[4];
+ aBuffer[j + aBufferSize * 5] += s * pan[5];
+ }
+ break;
+ }
+ break;
+ case 8:
+ switch (aVoice->mChannels)
+ {
+ case 8: // 8->8
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ pan[6] += pani[6];
+ pan[7] += pani[7];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ float s5 = aScratch[aBufferSize * 4 + j];
+ float s6 = aScratch[aBufferSize * 5 + j];
+ float s7 = aScratch[aBufferSize * 6 + j];
+ float s8 = aScratch[aBufferSize * 7 + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += s3 * pan[2];
+ aBuffer[j + aBufferSize * 3] += s4 * pan[3];
+ aBuffer[j + aBufferSize * 4] += s5 * pan[4];
+ aBuffer[j + aBufferSize * 5] += s6 * pan[5];
+ aBuffer[j + aBufferSize * 6] += s7 * pan[6];
+ aBuffer[j + aBufferSize * 7] += s8 * pan[7];
+ }
+ break;
+ case 6: // 6->8
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ pan[6] += pani[6];
+ pan[7] += pani[7];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ float s5 = aScratch[aBufferSize * 4 + j];
+ float s6 = aScratch[aBufferSize * 5 + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += s3 * pan[2];
+ aBuffer[j + aBufferSize * 3] += s4 * pan[3];
+ aBuffer[j + aBufferSize * 4] += 0.5f * (s5 + s1) * pan[4];
+ aBuffer[j + aBufferSize * 5] += 0.5f * (s6 + s2) * pan[5];
+ aBuffer[j + aBufferSize * 6] += s5 * pan[6];
+ aBuffer[j + aBufferSize * 7] += s6 * pan[7];
+ }
+ break;
+ case 4: // 4->8
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ pan[6] += pani[6];
+ pan[7] += pani[7];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ float s3 = aScratch[aBufferSize * 2 + j];
+ float s4 = aScratch[aBufferSize * 3 + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += 0.5f * (s1 + s2) * pan[2];
+ aBuffer[j + aBufferSize * 3] += 0.25f * (s1 + s2 + s3 + s4) * pan[3];
+ aBuffer[j + aBufferSize * 4] += 0.5f * (s1 + s3) * pan[4];
+ aBuffer[j + aBufferSize * 5] += 0.5f * (s2 + s4) * pan[5];
+ aBuffer[j + aBufferSize * 6] += s3 * pan[4];
+ aBuffer[j + aBufferSize * 7] += s4 * pan[5];
+ }
+ break;
+ case 2: // 2->8
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ pan[6] += pani[6];
+ pan[7] += pani[7];
+ float s1 = aScratch[j];
+ float s2 = aScratch[aBufferSize + j];
+ aBuffer[j + 0] += s1 * pan[0];
+ aBuffer[j + aBufferSize] += s2 * pan[1];
+ aBuffer[j + aBufferSize * 2] += 0.5f * (s1 + s2) * pan[2];
+ aBuffer[j + aBufferSize * 3] += 0.5f * (s1 + s2) * pan[3];
+ aBuffer[j + aBufferSize * 4] += s1 * pan[4];
+ aBuffer[j + aBufferSize * 5] += s2 * pan[5];
+ aBuffer[j + aBufferSize * 6] += s1 * pan[6];
+ aBuffer[j + aBufferSize * 7] += s2 * pan[7];
+ }
+ break;
+ case 1: // 1->8
+ for (j = 0; j < aSamplesToRead; j++)
+ {
+ pan[0] += pani[0];
+ pan[1] += pani[1];
+ pan[2] += pani[2];
+ pan[3] += pani[3];
+ pan[4] += pani[4];
+ pan[5] += pani[5];
+ pan[6] += pani[6];
+ pan[7] += pani[7];
+ float s = aScratch[j];
+ aBuffer[j + 0] += s * pan[0];
+ aBuffer[j + aBufferSize] += s * pan[1];
+ aBuffer[j + aBufferSize * 2] += s * pan[2];
+ aBuffer[j + aBufferSize * 3] += s * pan[3];
+ aBuffer[j + aBufferSize * 4] += s * pan[4];
+ aBuffer[j + aBufferSize * 5] += s * pan[5];
+ aBuffer[j + aBufferSize * 6] += s * pan[6];
+ aBuffer[j + aBufferSize * 7] += s * pan[7];
+ }
+ break;
+ }
+ break;
+ }
+
+ for (k = 0; k < aChannels; k++)
+ aVoice->mCurrentChannelVolume[k] = pand[k];
+ }
+
+ void Soloud::mixBus(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize, float *aScratch, unsigned int aBus, float aSamplerate, unsigned int aChannels)
+ {
+ unsigned int i, j;
+ // Clear accumulation buffer
+ for (i = 0; i < aSamplesToRead; i++)
+ {
+ for (j = 0; j < aChannels; j++)
+ {
+ aBuffer[i + j * aBufferSize] = 0;
+ }
+ }
+
+ // Accumulate sound sources
+ for (i = 0; i < mActiveVoiceCount; i++)
+ {
+ AudioSourceInstance *voice = mVoice[mActiveVoice[i]];
+ if (voice &&
+ voice->mBusHandle == aBus &&
+ !(voice->mFlags & AudioSourceInstance::PAUSED) &&
+ !(voice->mFlags & AudioSourceInstance::INAUDIBLE))
+ {
+ unsigned int j;
+ float step = voice->mSamplerate / aSamplerate;
+ // avoid step overflow
+ if (step > (1 << (32 - FIXPOINT_FRAC_BITS)))
+ step = 0;
+ unsigned int step_fixed = (int)floor(step * FIXPOINT_FRAC_MUL);
+ unsigned int outofs = 0;
+
+ if (voice->mDelaySamples)
+ {
+ if (voice->mDelaySamples > aSamplesToRead)
+ {
+ outofs = aSamplesToRead;
+ voice->mDelaySamples -= aSamplesToRead;
+ }
+ else
+ {
+ outofs = voice->mDelaySamples;
+ voice->mDelaySamples = 0;
+ }
+
+ // Clear scratch where we're skipping
+ for (j = 0; j < voice->mChannels; j++)
+ {
+ memset(aScratch + j * aBufferSize, 0, sizeof(float) * outofs);
+ }
+ }
+
+ while (step_fixed != 0 && outofs < aSamplesToRead)
+ {
+ if (voice->mLeftoverSamples == 0)
+ {
+ // Swap resample buffers (ping-pong)
+ AlignedFloatBuffer * t = voice->mResampleData[0];
+ voice->mResampleData[0] = voice->mResampleData[1];
+ voice->mResampleData[1] = t;
+
+ // Get a block of source data
+
+ int readcount = 0;
+ if (!voice->hasEnded() || voice->mFlags & AudioSourceInstance::LOOPING)
+ {
+ readcount = voice->getAudio(voice->mResampleData[0]->mData, SAMPLE_GRANULARITY, SAMPLE_GRANULARITY);
+ if (readcount < SAMPLE_GRANULARITY)
+ {
+ if (voice->mFlags & AudioSourceInstance::LOOPING)
+ {
+ while (readcount < SAMPLE_GRANULARITY && voice->seek(voice->mLoopPoint, mScratch.mData, mScratchSize) == SO_NO_ERROR)
+ {
+ voice->mLoopCount++;
+ int inc = voice->getAudio(voice->mResampleData[0]->mData + readcount, SAMPLE_GRANULARITY - readcount, SAMPLE_GRANULARITY);
+ readcount += inc;
+ if (inc == 0) break;
+ }
+ }
+ }
+ }
+
+ // Clear remaining of the resample data if the full scratch wasn't used
+ if (readcount < SAMPLE_GRANULARITY)
+ {
+ unsigned int i;
+ for (i = 0; i < voice->mChannels; i++)
+ memset(voice->mResampleData[0]->mData + readcount + SAMPLE_GRANULARITY * i, 0, sizeof(float) * (SAMPLE_GRANULARITY - readcount));
+ }
+
+ // If we go past zero, crop to zero (a bit of a kludge)
+ if (voice->mSrcOffset < SAMPLE_GRANULARITY * FIXPOINT_FRAC_MUL)
+ {
+ voice->mSrcOffset = 0;
+ }
+ else
+ {
+ // We have new block of data, move pointer backwards
+ voice->mSrcOffset -= SAMPLE_GRANULARITY * FIXPOINT_FRAC_MUL;
+ }
+
+
+ // Run the per-stream filters to get our source data
+
+ for (j = 0; j < FILTERS_PER_STREAM; j++)
+ {
+ if (voice->mFilter[j])
+ {
+ voice->mFilter[j]->filter(
+ voice->mResampleData[0]->mData,
+ SAMPLE_GRANULARITY,
+ voice->mChannels,
+ voice->mSamplerate,
+ mStreamTime);
+ }
+ }
+ }
+ else
+ {
+ voice->mLeftoverSamples = 0;
+ }
+
+ // Figure out how many samples we can generate from this source data.
+ // The value may be zero.
+
+ unsigned int writesamples = 0;
+
+ if (voice->mSrcOffset < SAMPLE_GRANULARITY * FIXPOINT_FRAC_MUL)
+ {
+ writesamples = ((SAMPLE_GRANULARITY * FIXPOINT_FRAC_MUL) - voice->mSrcOffset) / step_fixed + 1;
+
+ // avoid reading past the current buffer..
+ if (((writesamples * step_fixed + voice->mSrcOffset) >> FIXPOINT_FRAC_BITS) >= SAMPLE_GRANULARITY)
+ writesamples--;
+ }
+
+
+ // If this is too much for our output buffer, don't write that many:
+ if (writesamples + outofs > aSamplesToRead)
+ {
+ voice->mLeftoverSamples = (writesamples + outofs) - aSamplesToRead;
+ writesamples = aSamplesToRead - outofs;
+ }
+
+ // Call resampler to generate the samples, once per channel
+ if (writesamples)
+ {
+ for (j = 0; j < voice->mChannels; j++)
+ {
+ resample(voice->mResampleData[0]->mData + SAMPLE_GRANULARITY * j,
+ voice->mResampleData[1]->mData + SAMPLE_GRANULARITY * j,
+ aScratch + aBufferSize * j + outofs,
+ voice->mSrcOffset,
+ writesamples,
+ voice->mSamplerate,
+ aSamplerate,
+ step_fixed);
+ }
+ }
+
+ // Keep track of how many samples we've written so far
+ outofs += writesamples;
+
+ // Move source pointer onwards (writesamples may be zero)
+ voice->mSrcOffset += writesamples * step_fixed;
+ }
+
+ // Handle panning and channel expansion (and/or shrinking)
+ panAndExpand(voice, aBuffer, aSamplesToRead, aBufferSize, aScratch, aChannels);
+
+ // clear voice if the sound is over
+ if (!(voice->mFlags & AudioSourceInstance::LOOPING) && voice->hasEnded())
+ {
+ stopVoice(mActiveVoice[i]);
+ }
+ }
+ else
+ if (voice &&
+ voice->mBusHandle == aBus &&
+ !(voice->mFlags & AudioSourceInstance::PAUSED) &&
+ (voice->mFlags & AudioSourceInstance::INAUDIBLE) &&
+ (voice->mFlags & AudioSourceInstance::INAUDIBLE_TICK))
+ {
+ // Inaudible but needs ticking. Do minimal work (keep counters up to date and ask audiosource for data)
+ float step = voice->mSamplerate / aSamplerate;
+ int step_fixed = (int)floor(step * FIXPOINT_FRAC_MUL);
+ unsigned int outofs = 0;
+
+ if (voice->mDelaySamples)
+ {
+ if (voice->mDelaySamples > aSamplesToRead)
+ {
+ outofs = aSamplesToRead;
+ voice->mDelaySamples -= aSamplesToRead;
+ }
+ else
+ {
+ outofs = voice->mDelaySamples;
+ voice->mDelaySamples = 0;
+ }
+ }
+
+ while (step_fixed != 0 && outofs < aSamplesToRead)
+ {
+ if (voice->mLeftoverSamples == 0)
+ {
+ // Swap resample buffers (ping-pong)
+ AlignedFloatBuffer * t = voice->mResampleData[0];
+ voice->mResampleData[0] = voice->mResampleData[1];
+ voice->mResampleData[1] = t;
+
+ // Get a block of source data
+
+ int readcount = 0;
+ if (!voice->hasEnded() || voice->mFlags & AudioSourceInstance::LOOPING)
+ {
+ readcount = voice->getAudio(voice->mResampleData[0]->mData, SAMPLE_GRANULARITY, SAMPLE_GRANULARITY);
+ if (readcount < SAMPLE_GRANULARITY)
+ {
+ if (voice->mFlags & AudioSourceInstance::LOOPING)
+ {
+ while (readcount < SAMPLE_GRANULARITY && voice->seek(voice->mLoopPoint, mScratch.mData, mScratchSize) == SO_NO_ERROR)
+ {
+ voice->mLoopCount++;
+ readcount += voice->getAudio(voice->mResampleData[0]->mData + readcount, SAMPLE_GRANULARITY - readcount, SAMPLE_GRANULARITY);
+ }
+ }
+ }
+ }
+
+ // If we go past zero, crop to zero (a bit of a kludge)
+ if (voice->mSrcOffset < SAMPLE_GRANULARITY * FIXPOINT_FRAC_MUL)
+ {
+ voice->mSrcOffset = 0;
+ }
+ else
+ {
+ // We have new block of data, move pointer backwards
+ voice->mSrcOffset -= SAMPLE_GRANULARITY * FIXPOINT_FRAC_MUL;
+ }
+
+ // Skip filters
+ }
+ else
+ {
+ voice->mLeftoverSamples = 0;
+ }
+
+ // Figure out how many samples we can generate from this source data.
+ // The value may be zero.
+
+ unsigned int writesamples = 0;
+
+ if (voice->mSrcOffset < SAMPLE_GRANULARITY * FIXPOINT_FRAC_MUL)
+ {
+ writesamples = ((SAMPLE_GRANULARITY * FIXPOINT_FRAC_MUL) - voice->mSrcOffset) / step_fixed + 1;
+
+ // avoid reading past the current buffer..
+ if (((writesamples * step_fixed + voice->mSrcOffset) >> FIXPOINT_FRAC_BITS) >= SAMPLE_GRANULARITY)
+ writesamples--;
+ }
+
+
+ // If this is too much for our output buffer, don't write that many:
+ if (writesamples + outofs > aSamplesToRead)
+ {
+ voice->mLeftoverSamples = (writesamples + outofs) - aSamplesToRead;
+ writesamples = aSamplesToRead - outofs;
+ }
+
+ // Skip resampler
+
+ // Keep track of how many samples we've written so far
+ outofs += writesamples;
+
+ // Move source pointer onwards (writesamples may be zero)
+ voice->mSrcOffset += writesamples * step_fixed;
+ }
+
+ // clear voice if the sound is over
+ if (!(voice->mFlags & AudioSourceInstance::LOOPING) && voice->hasEnded())
+ {
+ stopVoice(mActiveVoice[i]);
+ }
+ }
+ }
+ }
+
+ void Soloud::mapResampleBuffers()
+ {
+ SOLOUD_ASSERT(mMaxActiveVoices < 256);
+ char live[256];
+ memset(live, 0, mMaxActiveVoices);
+ unsigned int i, j;
+ for (i = 0; i < mMaxActiveVoices; i++)
+ {
+ for (j = 0; j < mMaxActiveVoices; j++)
+ {
+ if (mResampleDataOwner[i] && mResampleDataOwner[i] == mVoice[mActiveVoice[j]])
+ {
+ live[i] |= 1; // Live channel
+ live[j] |= 2; // Live voice
+ }
+ }
+ }
+
+ for (i = 0; i < mMaxActiveVoices; i++)
+ {
+ if (!(live[i] & 1) && mResampleDataOwner[i]) // For all dead channels with owners..
+ {
+ mResampleDataOwner[i]->mResampleData[0] = 0;
+ mResampleDataOwner[i]->mResampleData[1] = 0;
+ mResampleDataOwner[i] = 0;
+ }
+ }
+
+ int latestfree = 0;
+ for (i = 0; i < mActiveVoiceCount; i++)
+ {
+ if (!(live[i] & 2) && mVoice[mActiveVoice[i]]) // For all live voices with no channel..
+ {
+ int found = -1;
+ for (j = latestfree; found == -1 && j < mMaxActiveVoices; j++)
+ {
+ if (mResampleDataOwner[j] == 0)
+ {
+ found = j;
+ }
+ }
+ SOLOUD_ASSERT(found != -1);
+ mResampleDataOwner[found] = mVoice[mActiveVoice[i]];
+ mResampleDataOwner[found]->mResampleData[0] = &mResampleData[found * 2 + 0];
+ mResampleDataOwner[found]->mResampleData[1] = &mResampleData[found * 2 + 1];
+ mResampleDataOwner[found]->mResampleData[0]->clear();
+ mResampleDataOwner[found]->mResampleData[1]->clear();
+ latestfree = found + 1;
+ }
+ }
+ }
+
+ void Soloud::calcActiveVoices()
+ {
+ // TODO: consider whether we need to re-evaluate the active voices all the time.
+ // It is a must when new voices are started, but otherwise we could get away
+ // with postponing it sometimes..
+
+ mActiveVoiceDirty = false;
+
+ // Populate
+ unsigned int i, candidates, mustlive;
+ candidates = 0;
+ mustlive = 0;
+ for (i = 0; i < mHighestVoice; i++)
+ {
+ if (mVoice[i] && (!(mVoice[i]->mFlags & (AudioSourceInstance::INAUDIBLE | AudioSourceInstance::PAUSED)) || (mVoice[i]->mFlags & AudioSourceInstance::INAUDIBLE_TICK)))
+ {
+ mActiveVoice[candidates] = i;
+ candidates++;
+ if (mVoice[i]->mFlags & AudioSourceInstance::INAUDIBLE_TICK)
+ {
+ mActiveVoice[candidates - 1] = mActiveVoice[mustlive];
+ mActiveVoice[mustlive] = i;
+ mustlive++;
+ }
+ }
+ }
+
+ // Check for early out
+ if (candidates <= mMaxActiveVoices)
+ {
+ // everything is audible, early out
+ mActiveVoiceCount = candidates;
+ mapResampleBuffers();
+ return;
+ }
+
+ mActiveVoiceCount = mMaxActiveVoices;
+
+ if (mustlive >= mMaxActiveVoices)
+ {
+ // Oopsie. Well, nothing to sort, since the "must live" voices already
+ // ate all our active voice slots.
+ // This is a potentially an error situation, but we have no way to report
+ // error from here. And asserting could be bad, too.
+ return;
+ }
+
+ // If we get this far, there's nothing to it: we'll have to sort the voices to find the most audible.
+
+ // Iterative partial quicksort:
+ int left = 0, stack[24], pos = 0, right;
+ int len = candidates - mustlive;
+ unsigned int *data = mActiveVoice + mustlive;
+ int k = mActiveVoiceCount;
+ for (;;)
+ {
+ for (; left + 1 < len; len++)
+ {
+ if (pos == 24) len = stack[pos = 0];
+ int pivot = data[left];
+ float pivotvol = mVoice[pivot]->mOverallVolume;
+ stack[pos++] = len;
+ for (right = left - 1;;)
+ {
+ do
+ {
+ right++;
+ }
+ while (mVoice[data[right]]->mOverallVolume > pivotvol);
+ do
+ {
+ len--;
+ }
+ while (pivotvol > mVoice[data[len]]->mOverallVolume);
+ if (right >= len) break;
+ int temp = data[right];
+ data[right] = data[len];
+ data[len] = temp;
+ }
+ }
+ if (pos == 0) break;
+ if (left >= k) break;
+ left = len;
+ len = stack[--pos];
+ }
+ // TODO: should the rest of the voices be flagged INAUDIBLE?
+ mapResampleBuffers();
+ }
+
+ void Soloud::mix_internal(unsigned int aSamples)
+ {
+#ifdef FLOATING_POINT_DEBUG
+ // This needs to be done in the audio thread as well..
+ static int done = 0;
+ if (!done)
+ {
+ unsigned int u;
+ u = _controlfp(0, 0);
+ u = u & ~(_EM_INVALID | /*_EM_DENORMAL |*/ _EM_ZERODIVIDE | _EM_OVERFLOW /*| _EM_UNDERFLOW | _EM_INEXACT*/);
+ _controlfp(u, _MCW_EM);
+ done = 1;
+ }
+#endif
+
+ float buffertime = aSamples / (float)mSamplerate;
+ float globalVolume[2];
+ mStreamTime += buffertime;
+ mLastClockedTime = 0;
+
+ globalVolume[0] = mGlobalVolume;
+ if (mGlobalVolumeFader.mActive)
+ {
+ mGlobalVolume = mGlobalVolumeFader.get(mStreamTime);
+ }
+ globalVolume[1] = mGlobalVolume;
+
+ lockAudioMutex();
+
+ // Process faders. May change scratch size.
+ int i;
+ for (i = 0; i < (signed)mHighestVoice; i++)
+ {
+ if (mVoice[i] && !(mVoice[i]->mFlags & AudioSourceInstance::PAUSED))
+ {
+ float volume[2];
+
+ mVoice[i]->mActiveFader = 0;
+
+ if (mGlobalVolumeFader.mActive > 0)
+ {
+ mVoice[i]->mActiveFader = 1;
+ }
+
+ mVoice[i]->mStreamTime += buffertime;
+ mVoice[i]->mStreamPosition += buffertime;
+
+ // TODO: this is actually unstable, because mStreamTime depends on the relative
+ // play speed.
+ if (mVoice[i]->mRelativePlaySpeedFader.mActive > 0)
+ {
+ float speed = mVoice[i]->mRelativePlaySpeedFader.get(mVoice[i]->mStreamTime);
+ setVoiceRelativePlaySpeed(i, speed);
+ }
+
+ volume[0] = mVoice[i]->mOverallVolume;
+ if (mVoice[i]->mVolumeFader.mActive > 0)
+ {
+ mVoice[i]->mSetVolume = mVoice[i]->mVolumeFader.get(mVoice[i]->mStreamTime);
+ mVoice[i]->mActiveFader = 1;
+ updateVoiceVolume(i);
+ mActiveVoiceDirty = true;
+ }
+ volume[1] = mVoice[i]->mOverallVolume;
+
+ if (mVoice[i]->mPanFader.mActive > 0)
+ {
+ float pan = mVoice[i]->mPanFader.get(mVoice[i]->mStreamTime);
+ setVoicePan(i, pan);
+ mVoice[i]->mActiveFader = 1;
+ }
+
+ if (mVoice[i]->mPauseScheduler.mActive)
+ {
+ mVoice[i]->mPauseScheduler.get(mVoice[i]->mStreamTime);
+ if (mVoice[i]->mPauseScheduler.mActive == -1)
+ {
+ mVoice[i]->mPauseScheduler.mActive = 0;
+ setVoicePause(i, 1);
+ }
+ }
+
+ if (mVoice[i]->mStopScheduler.mActive)
+ {
+ mVoice[i]->mStopScheduler.get(mVoice[i]->mStreamTime);
+ if (mVoice[i]->mStopScheduler.mActive == -1)
+ {
+ mVoice[i]->mStopScheduler.mActive = 0;
+ stopVoice(i);
+ }
+ }
+ }
+ }
+
+ if (mActiveVoiceDirty)
+ calcActiveVoices();
+
+ // Resize scratch if needed.
+ if (mScratchSize < mScratchNeeded)
+ {
+ mScratchSize = mScratchNeeded;
+ mScratch.init(mScratchSize * MAX_CHANNELS);
+ }
+
+ mixBus(mOutputScratch.mData, aSamples, aSamples, mScratch.mData, 0, (float)mSamplerate, mChannels);
+
+ for (i = 0; i < FILTERS_PER_STREAM; i++)
+ {
+ if (mFilterInstance[i])
+ {
+ mFilterInstance[i]->filter(mOutputScratch.mData, aSamples, mChannels, (float)mSamplerate, mStreamTime);
+ }
+ }
+
+ unlockAudioMutex();
+
+ clip(mOutputScratch, mScratch, aSamples, globalVolume[0], globalVolume[1]);
+
+ if (mFlags & ENABLE_VISUALIZATION)
+ {
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ mVisualizationChannelVolume[i] = 0;
+ }
+ if (aSamples > 255)
+ {
+ for (i = 0; i < 256; i++)
+ {
+ int j;
+ mVisualizationWaveData[i] = 0;
+ for (j = 0; j < (signed)mChannels; j++)
+ {
+ float sample = mScratch.mData[i + j * aSamples];
+ float absvol = (float)fabs(sample);
+ if (mVisualizationChannelVolume[j] < absvol)
+ mVisualizationChannelVolume[j] = absvol;
+ mVisualizationWaveData[i] += sample;
+ }
+ }
+ }
+ else
+ {
+ // Very unlikely failsafe branch
+ for (i = 0; i < 256; i++)
+ {
+ int j;
+ mVisualizationWaveData[i] = 0;
+ for (j = 0; j < (signed)mChannels; j++)
+ {
+ float sample = mScratch.mData[(i % aSamples) + j * aSamples];
+ float absvol = (float)fabs(sample);
+ if (mVisualizationChannelVolume[j] < absvol)
+ mVisualizationChannelVolume[j] = absvol;
+ mVisualizationWaveData[i] += sample;
+ }
+ }
+ }
+ }
+ }
+
+ void Soloud::mix(float *aBuffer, unsigned int aSamples)
+ {
+ mix_internal(aSamples);
+ interlace_samples_float(mScratch.mData, aBuffer, aSamples, mChannels);
+ }
+
+ void Soloud::mixSigned16(short *aBuffer, unsigned int aSamples)
+ {
+ mix_internal(aSamples);
+ interlace_samples_s16(mScratch.mData, aBuffer, aSamples, mChannels);
+ }
+
+ void deinterlace_samples_float(const float *aSourceBuffer, float *aDestBuffer, unsigned int aSamples, unsigned int aChannels)
+ {
+ // 121212 -> 111222
+ unsigned int i, j, c;
+ c = 0;
+ for (j = 0; j < aChannels; j++)
+ {
+ for (i = j; i < aSamples; i += aChannels)
+ {
+ aDestBuffer[c] = aSourceBuffer[i + j];
+ c++;
+ }
+ }
+ }
+
+ void interlace_samples_float(const float *aSourceBuffer, float *aDestBuffer, unsigned int aSamples, unsigned int aChannels)
+ {
+ // 111222 -> 121212
+ unsigned int i, j, c;
+ c = 0;
+ for (j = 0; j < aChannels; j++)
+ {
+ for (i = j; i < aSamples * aChannels; i += aChannels)
+ {
+ aDestBuffer[i] = aSourceBuffer[c];
+ c++;
+ }
+ }
+ }
+
+ void interlace_samples_s16(const float *aSourceBuffer, short *aDestBuffer, unsigned int aSamples, unsigned int aChannels)
+ {
+ // 111222 -> 121212
+ unsigned int i, j, c;
+ c = 0;
+ for (j = 0; j < aChannels; j++)
+ {
+ for (i = j; i < aSamples * aChannels; i += aChannels)
+ {
+ aDestBuffer[i] = (short)(aSourceBuffer[c] * 0x7fff);
+ c++;
+ }
+ }
+ }
+
+ void Soloud::lockAudioMutex()
+ {
+ if (mAudioThreadMutex)
+ {
+ Thread::lockMutex(mAudioThreadMutex);
+ }
+ SOLOUD_ASSERT(!mInsideAudioThreadMutex);
+ mInsideAudioThreadMutex = true;
+ }
+
+ void Soloud::unlockAudioMutex()
+ {
+ SOLOUD_ASSERT(mInsideAudioThreadMutex);
+ mInsideAudioThreadMutex = false;
+ if (mAudioThreadMutex)
+ {
+ Thread::unlockMutex(mAudioThreadMutex);
+ }
+ }
+
+};
diff --git a/src/soloud/src/core/soloud_audiosource.cpp b/src/soloud/src/core/soloud_audiosource.cpp
new file mode 100644
index 0000000..1e89260
--- /dev/null
+++ b/src/soloud/src/core/soloud_audiosource.cpp
@@ -0,0 +1,330 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+
+ AudioSourceInstance3dData::AudioSourceInstance3dData()
+ {
+ m3dAttenuationModel = 0;
+ m3dAttenuationRolloff = 1;
+ m3dDopplerFactor = 1.0;
+ m3dMaxDistance = 1000000.0f;
+ m3dMinDistance = 0.0f;
+ m3dPosition[0] = 0;
+ m3dPosition[1] = 0;
+ m3dPosition[2] = 0;
+ m3dVelocity[0] = 0;
+ m3dVelocity[1] = 0;
+ m3dVelocity[2] = 0;
+ mCollider = 0;
+ mColliderData = 0;
+ mAttenuator = 0;
+ }
+
+ void AudioSourceInstance3dData::init(AudioSource &aSource)
+ {
+ m3dAttenuationModel = aSource.m3dAttenuationModel;
+ m3dAttenuationRolloff = aSource.m3dAttenuationRolloff;
+ m3dDopplerFactor = aSource.m3dDopplerFactor;
+ m3dMaxDistance = aSource.m3dMaxDistance;
+ m3dMinDistance = aSource.m3dMinDistance;
+ mCollider = aSource.mCollider;
+ mColliderData = aSource.mColliderData;
+ mAttenuator = aSource.mAttenuator;
+ m3dVolume = 1.0f;
+ mDopplerValue = 1.0f;
+ }
+
+ AudioSourceInstance::AudioSourceInstance()
+ {
+ mPlayIndex = 0;
+ mFlags = 0;
+ mPan = 0;
+ // Default all volumes to 1.0 so sound behind N mix busses isn't super quiet.
+ int i;
+ for (i = 0; i < MAX_CHANNELS; i++)
+ mChannelVolume[i] = 1.0f;
+ mSetVolume = 1.0f;
+ mBaseSamplerate = 44100.0f;
+ mSamplerate = 44100.0f;
+ mSetRelativePlaySpeed = 1.0f;
+ mStreamTime = 0.0f;
+ mStreamPosition = 0.0f;
+ mAudioSourceID = 0;
+ mActiveFader = 0;
+ mChannels = 1;
+ mBusHandle = ~0u;
+ mLoopCount = 0;
+ mLoopPoint = 0;
+ for (i = 0; i < FILTERS_PER_STREAM; i++)
+ {
+ mFilter[i] = NULL;
+ }
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ mCurrentChannelVolume[i] = 0;
+ }
+ // behind pointers because we swap between the two buffers
+ mResampleData[0] = 0;
+ mResampleData[1] = 0;
+ mSrcOffset = 0;
+ mLeftoverSamples = 0;
+ mDelaySamples = 0;
+
+ }
+
+ AudioSourceInstance::~AudioSourceInstance()
+ {
+ int i;
+ for (i = 0; i < FILTERS_PER_STREAM; i++)
+ {
+ delete mFilter[i];
+ }
+ }
+
+ void AudioSourceInstance::init(AudioSource &aSource, int aPlayIndex)
+ {
+ mPlayIndex = aPlayIndex;
+ mBaseSamplerate = aSource.mBaseSamplerate;
+ mSamplerate = mBaseSamplerate;
+ mChannels = aSource.mChannels;
+ mStreamTime = 0.0f;
+ mStreamPosition = 0.0f;
+ mLoopPoint = aSource.mLoopPoint;
+
+ if (aSource.mFlags & AudioSource::SHOULD_LOOP)
+ {
+ mFlags |= AudioSourceInstance::LOOPING;
+ }
+ if (aSource.mFlags & AudioSource::PROCESS_3D)
+ {
+ mFlags |= AudioSourceInstance::PROCESS_3D;
+ }
+ if (aSource.mFlags & AudioSource::LISTENER_RELATIVE)
+ {
+ mFlags |= AudioSourceInstance::LISTENER_RELATIVE;
+ }
+ if (aSource.mFlags & AudioSource::INAUDIBLE_KILL)
+ {
+ mFlags |= AudioSourceInstance::INAUDIBLE_KILL;
+ }
+ if (aSource.mFlags & AudioSource::INAUDIBLE_TICK)
+ {
+ mFlags |= AudioSourceInstance::INAUDIBLE_TICK;
+ }
+ }
+
+ result AudioSourceInstance::rewind()
+ {
+ return NOT_IMPLEMENTED;
+ }
+
+ result AudioSourceInstance::seek(double aSeconds, float *mScratch, unsigned int mScratchSize)
+ {
+ double offset = aSeconds - mStreamPosition;
+ if (offset <= 0)
+ {
+ if (rewind() != SO_NO_ERROR)
+ {
+ // can't do generic seek backwards unless we can rewind.
+ return NOT_IMPLEMENTED;
+ }
+ offset = aSeconds;
+ }
+ int samples_to_discard = (int)floor(mSamplerate * offset);
+
+ while (samples_to_discard)
+ {
+ int samples = mScratchSize / mChannels;
+ if (samples > samples_to_discard)
+ samples = samples_to_discard;
+ getAudio(mScratch, samples, samples);
+ samples_to_discard -= samples;
+ }
+ mStreamPosition = offset;
+ return SO_NO_ERROR;
+ }
+
+
+ AudioSource::AudioSource()
+ {
+ int i;
+ for (i = 0; i < FILTERS_PER_STREAM; i++)
+ {
+ mFilter[i] = 0;
+ }
+ mFlags = 0;
+ mBaseSamplerate = 44100;
+ mAudioSourceID = 0;
+ mSoloud = 0;
+ mChannels = 1;
+ m3dMinDistance = 1;
+ m3dMaxDistance = 1000000.0f;
+ m3dAttenuationRolloff = 1.0f;
+ m3dAttenuationModel = NO_ATTENUATION;
+ m3dDopplerFactor = 1.0f;
+ mCollider = 0;
+ mAttenuator = 0;
+ mColliderData = 0;
+ mVolume = 1;
+ mLoopPoint = 0;
+ }
+
+ AudioSource::~AudioSource()
+ {
+ stop();
+ }
+
+ void AudioSource::setVolume(float aVolume)
+ {
+ mVolume = aVolume;
+ }
+
+ void AudioSource::setLoopPoint(time aLoopPoint)
+ {
+ mLoopPoint = aLoopPoint;
+ }
+
+ time AudioSource::getLoopPoint()
+ {
+ return mLoopPoint;
+ }
+
+ void AudioSource::setLooping(bool aLoop)
+ {
+ if (aLoop)
+ {
+ mFlags |= SHOULD_LOOP;
+ }
+ else
+ {
+ mFlags &= ~SHOULD_LOOP;
+ }
+ }
+
+ void AudioSource::setSingleInstance(bool aSingleInstance)
+ {
+ if (aSingleInstance)
+ {
+ mFlags |= SINGLE_INSTANCE;
+ }
+ else
+ {
+ mFlags &= ~SINGLE_INSTANCE;
+ }
+ }
+
+ void AudioSource::setFilter(unsigned int aFilterId, Filter *aFilter)
+ {
+ if (aFilterId >= FILTERS_PER_STREAM)
+ return;
+ mFilter[aFilterId] = aFilter;
+ }
+
+ void AudioSource::stop()
+ {
+ if (mSoloud)
+ {
+ mSoloud->stopAudioSource(*this);
+ }
+ }
+
+ void AudioSource::set3dMinMaxDistance(float aMinDistance, float aMaxDistance)
+ {
+ m3dMinDistance = aMinDistance;
+ m3dMaxDistance = aMaxDistance;
+ }
+
+ void AudioSource::set3dAttenuation(unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+ {
+ m3dAttenuationModel = aAttenuationModel;
+ m3dAttenuationRolloff = aAttenuationRolloffFactor;
+ }
+
+ void AudioSource::set3dDopplerFactor(float aDopplerFactor)
+ {
+ m3dDopplerFactor = aDopplerFactor;
+ }
+
+ void AudioSource::set3dListenerRelative(bool aListenerRelative)
+ {
+ if (aListenerRelative)
+ {
+ mFlags |= LISTENER_RELATIVE;
+ }
+ else
+ {
+ mFlags &= ~LISTENER_RELATIVE;
+ }
+ }
+
+
+ void AudioSource::set3dDistanceDelay(bool aDistanceDelay)
+ {
+ if (aDistanceDelay)
+ {
+ mFlags |= DISTANCE_DELAY;
+ }
+ else
+ {
+ mFlags &= ~DISTANCE_DELAY;
+ }
+ }
+
+ void AudioSource::set3dCollider(AudioCollider *aCollider, int aUserData)
+ {
+ mCollider = aCollider;
+ mColliderData = aUserData;
+ }
+
+ void AudioSource::set3dAttenuator(AudioAttenuator *aAttenuator)
+ {
+ mAttenuator = aAttenuator;
+ }
+
+ void AudioSource::setInaudibleBehavior(bool aMustTick, bool aKill)
+ {
+ mFlags &= ~(AudioSource::INAUDIBLE_KILL | AudioSource::INAUDIBLE_TICK);
+ if (aMustTick)
+ {
+ mFlags |= AudioSource::INAUDIBLE_TICK;
+ }
+ if (aKill)
+ {
+ mFlags |= AudioSource::INAUDIBLE_KILL;
+ }
+ }
+
+
+ float AudioSourceInstance::getInfo(unsigned int aInfoKey)
+ {
+ return 0;
+ }
+
+
+};
+
diff --git a/src/soloud/src/core/soloud_bus.cpp b/src/soloud/src/core/soloud_bus.cpp
new file mode 100644
index 0000000..f1793a2
--- /dev/null
+++ b/src/soloud/src/core/soloud_bus.cpp
@@ -0,0 +1,318 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_fft.h"
+
+namespace SoLoud
+{
+ BusInstance::BusInstance(Bus *aParent)
+ {
+ mParent = aParent;
+ mScratchSize = 0;
+ mFlags |= PROTECTED | INAUDIBLE_TICK;
+ }
+
+ unsigned int BusInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ int handle = mParent->mChannelHandle;
+ if (handle == 0)
+ {
+ // Avoid reuse of scratch data if this bus hasn't played anything yet
+ unsigned int i;
+ for (i = 0; i < aBufferSize * mChannels; i++)
+ aBuffer[i] = 0;
+ return aSamplesToRead;
+ }
+
+ Soloud *s = mParent->mSoloud;
+ if (s->mScratchNeeded != mScratchSize)
+ {
+ mScratchSize = s->mScratchNeeded;
+ mScratch.init(mScratchSize * MAX_CHANNELS);
+ }
+
+ s->mixBus(aBuffer, aSamplesToRead, aBufferSize, mScratch.mData, handle, mSamplerate, mChannels);
+
+ int i;
+ if (mParent->mFlags & AudioSource::VISUALIZATION_DATA)
+ {
+ for (i = 0; i < MAX_CHANNELS; i++)
+ mVisualizationChannelVolume[i] = 0;
+
+ if (aSamplesToRead > 255)
+ {
+ for (i = 0; i < 256; i++)
+ {
+ int j;
+ mVisualizationWaveData[i] = 0;
+ for (j = 0; j < (signed)mChannels; j++)
+ {
+ float sample = aBuffer[i + aBufferSize * j];
+ float absvol = (float)fabs(sample);
+ if (absvol > mVisualizationChannelVolume[j])
+ mVisualizationChannelVolume[j] = absvol;
+ mVisualizationWaveData[i] += sample;
+ }
+ }
+ }
+ else
+ {
+ // Very unlikely failsafe branch
+ for (i = 0; i < 256; i++)
+ {
+ int j;
+ mVisualizationWaveData[i] = 0;
+ for (j = 0; j < (signed)mChannels; j++)
+ {
+ float sample = aBuffer[(i % aSamplesToRead) + aBufferSize * j];
+ float absvol = (float)fabs(sample);
+ if (absvol > mVisualizationChannelVolume[j])
+ mVisualizationChannelVolume[j] = absvol;
+ mVisualizationWaveData[i] += sample;
+ }
+ }
+ }
+ }
+ return aSamplesToRead;
+ }
+
+ bool BusInstance::hasEnded()
+ {
+ // Busses never stop for fear of going under 50mph.
+ return 0;
+ }
+
+ BusInstance::~BusInstance()
+ {
+ Soloud *s = mParent->mSoloud;
+ int i;
+ for (i = 0; i < (signed)s->mHighestVoice; i++)
+ {
+ if (s->mVoice[i] && s->mVoice[i]->mBusHandle == mParent->mChannelHandle)
+ {
+ s->stopVoice(i);
+ }
+ }
+ }
+
+ Bus::Bus()
+ {
+ mChannelHandle = 0;
+ mInstance = 0;
+ mChannels = 2;
+ }
+
+ BusInstance * Bus::createInstance()
+ {
+ if (mChannelHandle)
+ {
+ stop();
+ mChannelHandle = 0;
+ mInstance = 0;
+ }
+ mInstance = new BusInstance(this);
+ return mInstance;
+ }
+
+ void Bus::findBusHandle()
+ {
+ if (mChannelHandle == 0)
+ {
+ // Find the channel the bus is playing on to calculate handle..
+ int i;
+ for (i = 0; mChannelHandle == 0 && i < (signed)mSoloud->mHighestVoice; i++)
+ {
+ if (mSoloud->mVoice[i] == mInstance)
+ {
+ mChannelHandle = mSoloud->getHandleFromVoice(i);
+ }
+ }
+ }
+ }
+
+ handle Bus::play(AudioSource &aSound, float aVolume, float aPan, bool aPaused)
+ {
+ if (!mInstance || !mSoloud)
+ {
+ return 0;
+ }
+
+ findBusHandle();
+
+ if (mChannelHandle == 0)
+ {
+ return 0;
+ }
+ return mSoloud->play(aSound, aVolume, aPan, aPaused, mChannelHandle);
+ }
+
+
+ handle Bus::playClocked(time aSoundTime, AudioSource &aSound, float aVolume, float aPan)
+ {
+ if (!mInstance || !mSoloud)
+ {
+ return 0;
+ }
+
+ findBusHandle();
+
+ if (mChannelHandle == 0)
+ {
+ return 0;
+ }
+
+ return mSoloud->playClocked(aSoundTime, aSound, aVolume, aPan, mChannelHandle);
+ }
+
+ handle Bus::play3d(AudioSource &aSound, float aPosX, float aPosY, float aPosZ, float aVelX, float aVelY, float aVelZ, float aVolume, bool aPaused)
+ {
+ if (!mInstance || !mSoloud)
+ {
+ return 0;
+ }
+
+ findBusHandle();
+
+ if (mChannelHandle == 0)
+ {
+ return 0;
+ }
+ return mSoloud->play3d(aSound, aPosX, aPosY, aPosZ, aVelX, aVelY, aVelZ, aVolume, aPaused, mChannelHandle);
+ }
+
+ handle Bus::play3dClocked(time aSoundTime, AudioSource &aSound, float aPosX, float aPosY, float aPosZ, float aVelX, float aVelY, float aVelZ, float aVolume)
+ {
+ if (!mInstance || !mSoloud)
+ {
+ return 0;
+ }
+
+ findBusHandle();
+
+ if (mChannelHandle == 0)
+ {
+ return 0;
+ }
+ return mSoloud->play3dClocked(aSoundTime, aSound, aPosX, aPosY, aPosZ, aVelX, aVelY, aVelZ, aVolume, mChannelHandle);
+ }
+
+
+ void Bus::setFilter(unsigned int aFilterId, Filter *aFilter)
+ {
+ if (aFilterId >= FILTERS_PER_STREAM)
+ return;
+
+ mFilter[aFilterId] = aFilter;
+
+ if (mInstance)
+ {
+ mSoloud->lockAudioMutex();
+ delete mInstance->mFilter[aFilterId];
+ mInstance->mFilter[aFilterId] = 0;
+
+ if (aFilter)
+ {
+ mInstance->mFilter[aFilterId] = mFilter[aFilterId]->createInstance();
+ }
+ mSoloud->unlockAudioMutex();
+ }
+ }
+
+ result Bus::setChannels(unsigned int aChannels)
+ {
+ if (aChannels == 0 || aChannels == 3 || aChannels == 5 || aChannels > 7 || aChannels > MAX_CHANNELS)
+ return INVALID_PARAMETER;
+ mChannels = aChannels;
+ return SO_NO_ERROR;
+ }
+
+ void Bus::setVisualizationEnable(bool aEnable)
+ {
+ if (aEnable)
+ {
+ mFlags |= AudioSource::VISUALIZATION_DATA;
+ }
+ else
+ {
+ mFlags &= ~AudioSource::VISUALIZATION_DATA;
+ }
+ }
+
+ float * Bus::calcFFT()
+ {
+ if (mInstance && mSoloud)
+ {
+ mSoloud->lockAudioMutex();
+ float temp[1024];
+ int i;
+ for (i = 0; i < 256; i++)
+ {
+ temp[i*2] = mInstance->mVisualizationWaveData[i];
+ temp[i*2+1] = 0;
+ temp[i+512] = 0;
+ temp[i+768] = 0;
+ }
+ mSoloud->unlockAudioMutex();
+
+ SoLoud::FFT::fft1024(temp);
+
+ for (i = 0; i < 256; i++)
+ {
+ float real = temp[i * 2];
+ float imag = temp[i * 2 + 1];
+ mFFTData[i] = (float)sqrt(real*real+imag*imag);
+ }
+ }
+
+ return mFFTData;
+ }
+
+ float * Bus::getWave()
+ {
+ if (mInstance && mSoloud)
+ {
+ int i;
+ mSoloud->lockAudioMutex();
+ for (i = 0; i < 256; i++)
+ mWaveData[i] = mInstance->mVisualizationWaveData[i];
+ mSoloud->unlockAudioMutex();
+ }
+ return mWaveData;
+ }
+
+ float Bus::getApproximateVolume(unsigned int aChannel)
+ {
+ if (aChannel > mChannels)
+ return 0;
+ float vol = 0;
+ if (mInstance && mSoloud)
+ {
+ mSoloud->lockAudioMutex();
+ vol = mInstance->mVisualizationChannelVolume[aChannel];
+ mSoloud->unlockAudioMutex();
+ }
+ return vol;
+ }
+};
diff --git a/src/soloud/src/core/soloud_core_3d.cpp b/src/soloud/src/core/soloud_core_3d.cpp
new file mode 100644
index 0000000..7d2e435
--- /dev/null
+++ b/src/soloud/src/core/soloud_core_3d.cpp
@@ -0,0 +1,631 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include "soloud_internal.h"
+
+// 3d audio operations
+
+namespace SoLoud
+{
+ struct vec3
+ {
+ float mX, mY, mZ;
+
+ bool null()
+ {
+ if (mX == 0 && mY == 0 && mZ == 0)
+ return true;
+ return false;
+ }
+
+ void neg()
+ {
+ mX = -mX;
+ mY = -mY;
+ mZ = -mZ;
+ }
+
+ float mag()
+ {
+ return (float)sqrt(mX * mX + mY * mY + mZ * mZ);
+ }
+
+ void normalize()
+ {
+ float m = mag();
+ if (m == 0)
+ {
+ mX = mY = mZ = 0;
+ return;
+ }
+ mX /= m;
+ mY /= m;
+ mZ /= m;
+ }
+
+ float dot(vec3 a)
+ {
+ return mX * a.mX + mY * a.mY + mZ * a.mZ;
+ }
+
+ vec3 sub(vec3 a)
+ {
+ vec3 r;
+ r.mX = mX - a.mX;
+ r.mY = mY - a.mY;
+ r.mZ = mZ - a.mZ;
+ return r;
+ }
+
+ vec3 cross(vec3 a)
+ {
+ vec3 r;
+
+ r.mX = mY * a.mZ - a.mY * mZ;
+ r.mY = mZ * a.mX - a.mZ * mX;
+ r.mZ = mX * a.mY - a.mX * mY;
+
+ return r;
+ }
+ };
+
+ struct mat3
+ {
+ vec3 m[3];
+
+ vec3 mul(vec3 a)
+ {
+ vec3 r;
+
+ r.mX = m[0].mX * a.mX + m[0].mY * a.mY + m[0].mZ * a.mZ;
+ r.mY = m[1].mX * a.mX + m[1].mY * a.mY + m[1].mZ * a.mZ;
+ r.mZ = m[2].mX * a.mX + m[2].mY * a.mY + m[2].mZ * a.mZ;
+
+ return r;
+ }
+
+ void lookatRH(vec3 at, vec3 up)
+ {
+ vec3 z = at;
+ z.normalize();
+ vec3 x = up.cross(z);
+ x.normalize();
+ vec3 y = z.cross(x);
+ m[0] = x;
+ m[1] = y;
+ m[2] = z;
+ }
+
+ void lookatLH(vec3 at, vec3 up)
+ {
+ vec3 z = at;
+ z.normalize();
+ vec3 x = up.cross(z);
+ x.normalize();
+ vec3 y = z.cross(x);
+ x.neg(); // flip x
+ m[0] = x;
+ m[1] = y;
+ m[2] = z;
+ }
+ };
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b)) ? (a) : (b)
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b)) ? (a) : (b)
+#endif
+
+ float doppler(vec3 aDeltaPos, vec3 aSrcVel, vec3 aDstVel, float aFactor, float aSoundSpeed)
+ {
+ float deltamag = aDeltaPos.mag();
+ if (deltamag == 0)
+ return 1.0f;
+ float vls = aDeltaPos.dot(aDstVel) / deltamag;
+ float vss = aDeltaPos.dot(aSrcVel) / deltamag;
+ float maxspeed = aSoundSpeed / aFactor;
+ vss = MIN(vss, maxspeed);
+ vls = MIN(vls, maxspeed);
+ return (aSoundSpeed - aFactor * vls) / (aSoundSpeed - aFactor * vss);
+ }
+
+ float attenuateInvDistance(float aDistance, float aMinDistance, float aMaxDistance, float aRolloffFactor)
+ {
+ float distance = MAX(aDistance, aMinDistance);
+ distance = MIN(distance, aMaxDistance);
+ return aMinDistance / (aMinDistance + aRolloffFactor * (distance - aMinDistance));
+ }
+
+ float attenuateLinearDistance(float aDistance, float aMinDistance, float aMaxDistance, float aRolloffFactor)
+ {
+ float distance = MAX(aDistance, aMinDistance);
+ distance = MIN(distance, aMaxDistance);
+ return 1 - aRolloffFactor * (distance - aMinDistance) / (aMaxDistance - aMinDistance);
+ }
+
+ float attenuateExponentialDistance(float aDistance, float aMinDistance, float aMaxDistance, float aRolloffFactor)
+ {
+ float distance = MAX(aDistance, aMinDistance);
+ distance = MIN(distance, aMaxDistance);
+ return (float)pow(distance / aMinDistance, -aRolloffFactor);
+ }
+
+ void Soloud::update3dVoices(unsigned int *aVoiceArray, unsigned int aVoiceCount)
+ {
+ vec3 speaker[MAX_CHANNELS];
+
+ int i;
+ for (i = 0; i < (signed)mChannels; i++)
+ {
+ speaker[i].mX = m3dSpeakerPosition[3 * i + 0];
+ speaker[i].mY = m3dSpeakerPosition[3 * i + 1];
+ speaker[i].mZ = m3dSpeakerPosition[3 * i + 2];
+ speaker[i].normalize();
+ }
+ for (; i < MAX_CHANNELS; i++)
+ {
+ speaker[i].mX = 0;
+ speaker[i].mY = 0;
+ speaker[i].mZ = 0;
+ }
+
+ vec3 lpos, lvel, at, up;
+ at.mX = m3dAt[0];
+ at.mY = m3dAt[1];
+ at.mZ = m3dAt[2];
+ up.mX = m3dUp[0];
+ up.mY = m3dUp[1];
+ up.mZ = m3dUp[2];
+ lpos.mX = m3dPosition[0];
+ lpos.mY = m3dPosition[1];
+ lpos.mZ = m3dPosition[2];
+ lvel.mX = m3dVelocity[0];
+ lvel.mY = m3dVelocity[1];
+ lvel.mZ = m3dVelocity[2];
+ mat3 m;
+ if (mFlags & LEFT_HANDED_3D)
+ {
+ m.lookatLH(at, up);
+ }
+ else
+ {
+ m.lookatRH(at, up);
+ }
+
+ for (i = 0; i < (signed)aVoiceCount; i++)
+ {
+ AudioSourceInstance3dData * v = &m3dData[aVoiceArray[i]];
+
+ float vol = 1;
+
+ // custom collider
+ if (v->mCollider)
+ {
+ vol *= v->mCollider->collide(this, v, v->mColliderData);
+ }
+
+ vec3 pos, vel;
+ pos.mX = v->m3dPosition[0];
+ pos.mY = v->m3dPosition[1];
+ pos.mZ = v->m3dPosition[2];
+
+ vel.mX = v->m3dVelocity[0];
+ vel.mY = v->m3dVelocity[1];
+ vel.mZ = v->m3dVelocity[2];
+
+ if (!(v->mFlags & AudioSourceInstance::LISTENER_RELATIVE))
+ {
+ pos = pos.sub(lpos);
+ }
+
+ float dist = pos.mag();
+
+ // attenuation
+
+ if (v->mAttenuator)
+ {
+ vol *= v->mAttenuator->attenuate(dist, v->m3dMinDistance, v->m3dMaxDistance, v->m3dAttenuationRolloff);
+ }
+ else
+ {
+ switch (v->m3dAttenuationModel)
+ {
+ case AudioSource::INVERSE_DISTANCE:
+ vol *= attenuateInvDistance(dist, v->m3dMinDistance, v->m3dMaxDistance, v->m3dAttenuationRolloff);
+ break;
+ case AudioSource::LINEAR_DISTANCE:
+ vol *= attenuateLinearDistance(dist, v->m3dMinDistance, v->m3dMaxDistance, v->m3dAttenuationRolloff);
+ break;
+ case AudioSource::EXPONENTIAL_DISTANCE:
+ vol *= attenuateExponentialDistance(dist, v->m3dMinDistance, v->m3dMaxDistance, v->m3dAttenuationRolloff);
+ break;
+ default:
+ //case AudioSource::NO_ATTENUATION:
+ break;
+ }
+ }
+
+ // cone
+
+ // (todo) vol *= conev;
+
+ // doppler
+ v->mDopplerValue = doppler(pos, vel, lvel, v->m3dDopplerFactor, m3dSoundSpeed);
+
+ // panning
+ pos = m.mul(pos);
+ pos.normalize();
+
+ // Apply volume to channels based on speaker vectors
+ int j;
+ for (j = 0; j < (signed)mChannels; j++)
+ {
+ float speakervol = (speaker[j].dot(pos) + 1) / 2;
+ if (speaker[j].null())
+ speakervol = 1;
+ // Different speaker "focus" calculations to try, if the default "bleeds" too much..
+ //speakervol = (speakervol * speakervol + speakervol) / 2;
+ //speakervol = speakervol * speakervol;
+ v->mChannelVolume[j] = vol * speakervol;
+ }
+ for (; j < MAX_CHANNELS; j++)
+ {
+ v->mChannelVolume[j] = 0;
+ }
+
+ v->m3dVolume = vol;
+ }
+ }
+
+ void Soloud::update3dAudio()
+ {
+ unsigned int voicecount = 0;
+ unsigned int voices[VOICE_COUNT];
+
+ // Step 1 - find voices that need 3d processing
+ lockAudioMutex();
+ int i;
+ for (i = 0; i < (signed)mHighestVoice; i++)
+ {
+ if (mVoice[i] && mVoice[i]->mFlags & AudioSourceInstance::PROCESS_3D)
+ {
+ voices[voicecount] = i;
+ voicecount++;
+ m3dData[i].mFlags = mVoice[i]->mFlags;
+ }
+ }
+ unlockAudioMutex();
+
+ // Step 2 - do 3d processing
+
+ update3dVoices(voices, voicecount);
+
+ // Step 3 - update SoLoud voices
+
+ lockAudioMutex();
+ for (i = 0; i < (int)voicecount; i++)
+ {
+ AudioSourceInstance3dData * v = &m3dData[voices[i]];
+ AudioSourceInstance * vi = mVoice[voices[i]];
+ if (vi)
+ {
+ updateVoiceRelativePlaySpeed(voices[i]);
+ updateVoiceVolume(voices[i]);
+ int j;
+ for (j = 0; j < MAX_CHANNELS; j++)
+ {
+ vi->mChannelVolume[j] = v->mChannelVolume[j];
+ }
+
+ if (vi->mOverallVolume < 0.01f)
+ {
+ // Inaudible.
+ vi->mFlags |= AudioSourceInstance::INAUDIBLE;
+
+ if (vi->mFlags & AudioSourceInstance::INAUDIBLE_KILL)
+ {
+ stopVoice(voices[i]);
+ }
+ }
+ else
+ {
+ vi->mFlags &= ~AudioSourceInstance::INAUDIBLE;
+ }
+ }
+ }
+
+ mActiveVoiceDirty = true;
+ unlockAudioMutex();
+ }
+
+
+ handle Soloud::play3d(AudioSource &aSound, float aPosX, float aPosY, float aPosZ, float aVelX, float aVelY, float aVelZ, float aVolume, bool aPaused, unsigned int aBus)
+ {
+ handle h = play(aSound, aVolume, 0, 1, aBus);
+ lockAudioMutex();
+ int v = getVoiceFromHandle(h);
+ if (v < 0)
+ {
+ unlockAudioMutex();
+ return h;
+ }
+ m3dData[v].mHandle = h;
+ mVoice[v]->mFlags |= AudioSourceInstance::PROCESS_3D;
+ set3dSourceParameters(h, aPosX, aPosY, aPosZ, aVelX, aVelY, aVelZ);
+
+ int samples = 0;
+ if (aSound.mFlags & AudioSource::DISTANCE_DELAY)
+ {
+ vec3 pos;
+ pos.mX = aPosX;
+ pos.mY = aPosY;
+ pos.mZ = aPosZ;
+ if (!(mVoice[v]->mFlags & AudioSource::LISTENER_RELATIVE))
+ {
+ pos.mX -= m3dPosition[0];
+ pos.mY -= m3dPosition[1];
+ pos.mZ -= m3dPosition[2];
+ }
+ float dist = pos.mag();
+ samples += (int)floor((dist / m3dSoundSpeed) * mSamplerate);
+ }
+
+ update3dVoices((unsigned int *)&v, 1);
+ updateVoiceRelativePlaySpeed(v);
+ int j;
+ for (j = 0; j < MAX_CHANNELS; j++)
+ {
+ mVoice[v]->mChannelVolume[j] = m3dData[v].mChannelVolume[j];
+ }
+
+ updateVoiceVolume(v);
+
+ // Fix initial voice volume ramp up
+ int i;
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ mVoice[v]->mCurrentChannelVolume[i] = mVoice[v]->mChannelVolume[i] * mVoice[v]->mOverallVolume;
+ }
+
+ if (mVoice[v]->mOverallVolume < 0.01f)
+ {
+ // Inaudible.
+ mVoice[v]->mFlags |= AudioSourceInstance::INAUDIBLE;
+
+ if (mVoice[v]->mFlags & AudioSourceInstance::INAUDIBLE_KILL)
+ {
+ stopVoice(v);
+ }
+ }
+ else
+ {
+ mVoice[v]->mFlags &= ~AudioSourceInstance::INAUDIBLE;
+ }
+ mActiveVoiceDirty = true;
+
+ unlockAudioMutex();
+ setDelaySamples(h, samples);
+ setPause(h, aPaused);
+ return h;
+ }
+
+ handle Soloud::play3dClocked(time aSoundTime, AudioSource &aSound, float aPosX, float aPosY, float aPosZ, float aVelX, float aVelY, float aVelZ, float aVolume, unsigned int aBus)
+ {
+ handle h = play(aSound, aVolume, 0, 1, aBus);
+ lockAudioMutex();
+ int v = getVoiceFromHandle(h);
+ if (v < 0)
+ {
+ unlockAudioMutex();
+ return h;
+ }
+ m3dData[v].mHandle = h;
+ mVoice[v]->mFlags |= AudioSourceInstance::PROCESS_3D;
+ set3dSourceParameters(h, aPosX, aPosY, aPosZ, aVelX, aVelY, aVelZ);
+ time lasttime = mLastClockedTime;
+ if (lasttime == 0)
+ mLastClockedTime = aSoundTime;
+ vec3 pos;
+ pos.mX = aPosX;
+ pos.mY = aPosY;
+ pos.mZ = aPosZ;
+ unlockAudioMutex();
+ int samples = 0;
+ if (lasttime != 0)
+ {
+ samples = (int)floor((aSoundTime - lasttime) * mSamplerate);
+ }
+ if (aSound.mFlags & AudioSource::DISTANCE_DELAY)
+ {
+ float dist = pos.mag();
+ samples += (int)floor((dist / m3dSoundSpeed) * mSamplerate);
+ }
+
+ update3dVoices((unsigned int *)&v, 1);
+ lockAudioMutex();
+ updateVoiceRelativePlaySpeed(v);
+ int j;
+ for (j = 0; j < MAX_CHANNELS; j++)
+ {
+ mVoice[v]->mChannelVolume[j] = m3dData[v].mChannelVolume[j];
+ }
+
+ updateVoiceVolume(v);
+
+ // Fix initial voice volume ramp up
+ int i;
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ mVoice[v]->mCurrentChannelVolume[i] = mVoice[v]->mChannelVolume[i] * mVoice[v]->mOverallVolume;
+ }
+
+ if (mVoice[v]->mOverallVolume < 0.01f)
+ {
+ // Inaudible.
+ mVoice[v]->mFlags |= AudioSourceInstance::INAUDIBLE;
+
+ if (mVoice[v]->mFlags & AudioSourceInstance::INAUDIBLE_KILL)
+ {
+ stopVoice(v);
+ }
+ }
+ else
+ {
+ mVoice[v]->mFlags &= ~AudioSourceInstance::INAUDIBLE;
+ }
+ mActiveVoiceDirty = true;
+ unlockAudioMutex();
+
+ setDelaySamples(h, samples);
+ setPause(h, 0);
+ return h;
+ }
+
+
+
+ result Soloud::set3dSoundSpeed(float aSpeed)
+ {
+ if (aSpeed <= 0)
+ return INVALID_PARAMETER;
+ m3dSoundSpeed = aSpeed;
+ return SO_NO_ERROR;
+ }
+
+
+ float Soloud::get3dSoundSpeed()
+ {
+ return m3dSoundSpeed;
+ }
+
+
+ void Soloud::set3dListenerParameters(float aPosX, float aPosY, float aPosZ, float aAtX, float aAtY, float aAtZ, float aUpX, float aUpY, float aUpZ, float aVelocityX, float aVelocityY, float aVelocityZ)
+ {
+ m3dPosition[0] = aPosX;
+ m3dPosition[1] = aPosY;
+ m3dPosition[2] = aPosZ;
+ m3dAt[0] = aAtX;
+ m3dAt[1] = aAtY;
+ m3dAt[2] = aAtZ;
+ m3dUp[0] = aUpX;
+ m3dUp[1] = aUpY;
+ m3dUp[2] = aUpZ;
+ m3dVelocity[0] = aVelocityX;
+ m3dVelocity[1] = aVelocityY;
+ m3dVelocity[2] = aVelocityZ;
+ }
+
+
+ void Soloud::set3dListenerPosition(float aPosX, float aPosY, float aPosZ)
+ {
+ m3dPosition[0] = aPosX;
+ m3dPosition[1] = aPosY;
+ m3dPosition[2] = aPosZ;
+ }
+
+
+ void Soloud::set3dListenerAt(float aAtX, float aAtY, float aAtZ)
+ {
+ m3dAt[0] = aAtX;
+ m3dAt[1] = aAtY;
+ m3dAt[2] = aAtZ;
+ }
+
+
+ void Soloud::set3dListenerUp(float aUpX, float aUpY, float aUpZ)
+ {
+ m3dUp[0] = aUpX;
+ m3dUp[1] = aUpY;
+ m3dUp[2] = aUpZ;
+ }
+
+
+ void Soloud::set3dListenerVelocity(float aVelocityX, float aVelocityY, float aVelocityZ)
+ {
+ m3dVelocity[0] = aVelocityX;
+ m3dVelocity[1] = aVelocityY;
+ m3dVelocity[2] = aVelocityZ;
+ }
+
+
+ void Soloud::set3dSourceParameters(handle aVoiceHandle, float aPosX, float aPosY, float aPosZ, float aVelocityX, float aVelocityY, float aVelocityZ)
+ {
+ FOR_ALL_VOICES_PRE_3D
+ m3dData[ch].m3dPosition[0] = aPosX;
+ m3dData[ch].m3dPosition[1] = aPosY;
+ m3dData[ch].m3dPosition[2] = aPosZ;
+ m3dData[ch].m3dVelocity[0] = aVelocityX;
+ m3dData[ch].m3dVelocity[1] = aVelocityY;
+ m3dData[ch].m3dVelocity[2] = aVelocityZ;
+ FOR_ALL_VOICES_POST_3D
+ }
+
+
+ void Soloud::set3dSourcePosition(handle aVoiceHandle, float aPosX, float aPosY, float aPosZ)
+ {
+ FOR_ALL_VOICES_PRE_3D
+ m3dData[ch].m3dPosition[0] = aPosX;
+ m3dData[ch].m3dPosition[1] = aPosY;
+ m3dData[ch].m3dPosition[2] = aPosZ;
+ FOR_ALL_VOICES_POST_3D
+ }
+
+
+ void Soloud::set3dSourceVelocity(handle aVoiceHandle, float aVelocityX, float aVelocityY, float aVelocityZ)
+ {
+ FOR_ALL_VOICES_PRE_3D
+ m3dData[ch].m3dVelocity[0] = aVelocityX;
+ m3dData[ch].m3dVelocity[1] = aVelocityY;
+ m3dData[ch].m3dVelocity[2] = aVelocityZ;
+ FOR_ALL_VOICES_POST_3D
+ }
+
+
+ void Soloud::set3dSourceMinMaxDistance(handle aVoiceHandle, float aMinDistance, float aMaxDistance)
+ {
+ FOR_ALL_VOICES_PRE_3D
+ m3dData[ch].m3dMinDistance = aMinDistance;
+ m3dData[ch].m3dMaxDistance = aMaxDistance;
+ FOR_ALL_VOICES_POST_3D
+ }
+
+
+ void Soloud::set3dSourceAttenuation(handle aVoiceHandle, unsigned int aAttenuationModel, float aAttenuationRolloffFactor)
+ {
+ FOR_ALL_VOICES_PRE_3D
+ m3dData[ch].m3dAttenuationModel = aAttenuationModel;
+ m3dData[ch].m3dAttenuationRolloff = aAttenuationRolloffFactor;
+ FOR_ALL_VOICES_POST_3D
+ }
+
+
+ void Soloud::set3dSourceDopplerFactor(handle aVoiceHandle, float aDopplerFactor)
+ {
+ FOR_ALL_VOICES_PRE_3D
+ m3dData[ch].m3dDopplerFactor = aDopplerFactor;
+ FOR_ALL_VOICES_POST_3D
+ }
+};
diff --git a/src/soloud/src/core/soloud_core_basicops.cpp b/src/soloud/src/core/soloud_core_basicops.cpp
new file mode 100644
index 0000000..26b025f
--- /dev/null
+++ b/src/soloud/src/core/soloud_core_basicops.cpp
@@ -0,0 +1,206 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include "soloud_internal.h"
+
+// Core "basic" operations - play, stop, etc
+
+namespace SoLoud
+{
+ handle Soloud::play(AudioSource &aSound, float aVolume, float aPan, bool aPaused, unsigned int aBus)
+ {
+ if (aSound.mFlags & AudioSource::SINGLE_INSTANCE)
+ {
+ // Only one instance allowed, stop others
+ aSound.stop();
+ }
+
+ // Creation of an audio instance may take significant amount of time,
+ // so let's not do it inside the audio thread mutex.
+ aSound.mSoloud = this;
+ SoLoud::AudioSourceInstance *instance = aSound.createInstance();
+
+ lockAudioMutex();
+ int ch = findFreeVoice();
+ if (ch < 0)
+ {
+ unlockAudioMutex();
+ delete instance;
+ return UNKNOWN_ERROR;
+ }
+ if (!aSound.mAudioSourceID)
+ {
+ aSound.mAudioSourceID = mAudioSourceID;
+ mAudioSourceID++;
+ }
+ mVoice[ch] = instance;
+ mVoice[ch]->mAudioSourceID = aSound.mAudioSourceID;
+ mVoice[ch]->mBusHandle = aBus;
+ mVoice[ch]->init(aSound, mPlayIndex);
+ m3dData[ch].init(aSound);
+
+ mPlayIndex++;
+
+ // 20 bits, skip the last one (top bits full = voice group)
+ if (mPlayIndex == 0xfffff)
+ {
+ mPlayIndex = 0;
+ }
+
+ if (aPaused)
+ {
+ mVoice[ch]->mFlags |= AudioSourceInstance::PAUSED;
+ }
+
+ setVoicePan(ch, aPan);
+ if (aVolume < 0)
+ {
+ setVoiceVolume(ch, aSound.mVolume);
+ }
+ else
+ {
+ setVoiceVolume(ch, aVolume);
+ }
+
+ // Fix initial voice volume ramp up
+ int i;
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ mVoice[ch]->mCurrentChannelVolume[i] = mVoice[ch]->mChannelVolume[i] * mVoice[ch]->mOverallVolume;
+ }
+
+ setVoiceRelativePlaySpeed(ch, 1);
+
+ for (i = 0; i < FILTERS_PER_STREAM; i++)
+ {
+ if (aSound.mFilter[i])
+ {
+ mVoice[ch]->mFilter[i] = aSound.mFilter[i]->createInstance();
+ }
+ }
+
+ mActiveVoiceDirty = true;
+
+ unlockAudioMutex();
+
+ int handle = getHandleFromVoice(ch);
+ return handle;
+ }
+
+ handle Soloud::playClocked(time aSoundTime, AudioSource &aSound, float aVolume, float aPan, unsigned int aBus)
+ {
+ handle h = play(aSound, aVolume, aPan, 1, aBus);
+ lockAudioMutex();
+ time lasttime = mLastClockedTime;
+ if (lasttime == 0)
+ mLastClockedTime = aSoundTime;
+ unlockAudioMutex();
+ int samples = 0;
+ if (aSoundTime > lasttime)
+ {
+ samples = (int)floor((aSoundTime - lasttime) * mSamplerate);
+ }
+ setDelaySamples(h, samples);
+ setPause(h, 0);
+ return h;
+ }
+
+ handle Soloud::playBackground(AudioSource &aSound, float aVolume, bool aPaused, unsigned int aBus)
+ {
+ handle h = play(aSound, aVolume, 0.0f, aPaused, aBus);
+ setPanAbsolute(h, 1.0f, 1.0f);
+ return h;
+ }
+
+ result Soloud::seek(handle aVoiceHandle, time aSeconds)
+ {
+ result res = SO_NO_ERROR;
+ result singleres = SO_NO_ERROR;
+ FOR_ALL_VOICES_PRE
+ singleres = mVoice[ch]->seek(aSeconds, mScratch.mData, mScratchSize);
+ if (singleres != SO_NO_ERROR)
+ res = singleres;
+ FOR_ALL_VOICES_POST
+ return res;
+ }
+
+
+ void Soloud::stop(handle aVoiceHandle)
+ {
+ FOR_ALL_VOICES_PRE
+ stopVoice(ch);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::stopAudioSource(AudioSource &aSound)
+ {
+ if (aSound.mAudioSourceID)
+ {
+ lockAudioMutex();
+
+ int i;
+ for (i = 0; i < (signed)mHighestVoice; i++)
+ {
+ if (mVoice[i] && mVoice[i]->mAudioSourceID == aSound.mAudioSourceID)
+ {
+ stopVoice(i);
+ }
+ }
+ unlockAudioMutex();
+ }
+ }
+
+ void Soloud::stopAll()
+ {
+ int i;
+ lockAudioMutex();
+ for (i = 0; i < (signed)mHighestVoice; i++)
+ {
+ stopVoice(i);
+ }
+ unlockAudioMutex();
+ }
+
+ int Soloud::countAudioSource(AudioSource &aSound)
+ {
+ int count = 0;
+ if (aSound.mAudioSourceID)
+ {
+ lockAudioMutex();
+
+ int i;
+ for (i = 0; i < (signed)mHighestVoice; i++)
+ {
+ if (mVoice[i] && mVoice[i]->mAudioSourceID == aSound.mAudioSourceID)
+ {
+ count++;
+ }
+ }
+ unlockAudioMutex();
+ }
+ return count;
+ }
+
+}
diff --git a/src/soloud/src/core/soloud_core_faderops.cpp b/src/soloud/src/core/soloud_core_faderops.cpp
new file mode 100644
index 0000000..1f68847
--- /dev/null
+++ b/src/soloud/src/core/soloud_core_faderops.cpp
@@ -0,0 +1,156 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud_internal.h"
+
+// Core operations related to faders (not including filters)
+
+namespace SoLoud
+{
+ void Soloud::schedulePause(handle aVoiceHandle, time aTime)
+ {
+ if (aTime <= 0)
+ {
+ setPause(aVoiceHandle, 1);
+ return;
+ }
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mPauseScheduler.set(1, 0, aTime, mVoice[ch]->mStreamTime);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::scheduleStop(handle aVoiceHandle, time aTime)
+ {
+ if (aTime <= 0)
+ {
+ stop(aVoiceHandle);
+ return;
+ }
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mStopScheduler.set(1, 0, aTime, mVoice[ch]->mStreamTime);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::fadeVolume(handle aVoiceHandle, float aTo, time aTime)
+ {
+ float from = getVolume(aVoiceHandle);
+ if (aTime <= 0 || aTo == from)
+ {
+ setVolume(aVoiceHandle, aTo);
+ return;
+ }
+
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mVolumeFader.set(from, aTo, aTime, mVoice[ch]->mStreamTime);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::fadePan(handle aVoiceHandle, float aTo, time aTime)
+ {
+ float from = getPan(aVoiceHandle);
+ if (aTime <= 0 || aTo == from)
+ {
+ setPan(aVoiceHandle, aTo);
+ return;
+ }
+
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mPanFader.set(from, aTo, aTime, mVoice[ch]->mStreamTime);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::fadeRelativePlaySpeed(handle aVoiceHandle, float aTo, time aTime)
+ {
+ float from = getRelativePlaySpeed(aVoiceHandle);
+ if (aTime <= 0 || aTo == from)
+ {
+ setRelativePlaySpeed(aVoiceHandle, aTo);
+ return;
+ }
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mRelativePlaySpeedFader.set(from, aTo, aTime, mVoice[ch]->mStreamTime);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::fadeGlobalVolume(float aTo, time aTime)
+ {
+ float from = getGlobalVolume();
+ if (aTime <= 0 || aTo == from)
+ {
+ setGlobalVolume(aTo);
+ return;
+ }
+ mGlobalVolumeFader.set(from, aTo, aTime, mStreamTime);
+ }
+
+
+ void Soloud::oscillateVolume(handle aVoiceHandle, float aFrom, float aTo, time aTime)
+ {
+ if (aTime <= 0 || aTo == aFrom)
+ {
+ setVolume(aVoiceHandle, aTo);
+ return;
+ }
+
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mVolumeFader.setLFO(aFrom, aTo, aTime, mVoice[ch]->mStreamTime);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::oscillatePan(handle aVoiceHandle, float aFrom, float aTo, time aTime)
+ {
+ if (aTime <= 0 || aTo == aFrom)
+ {
+ setPan(aVoiceHandle, aTo);
+ return;
+ }
+
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mPanFader.setLFO(aFrom, aTo, aTime, mVoice[ch]->mStreamTime);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::oscillateRelativePlaySpeed(handle aVoiceHandle, float aFrom, float aTo, time aTime)
+ {
+ if (aTime <= 0 || aTo == aFrom)
+ {
+ setRelativePlaySpeed(aVoiceHandle, aTo);
+ return;
+ }
+
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mRelativePlaySpeedFader.setLFO(aFrom, aTo, aTime, mVoice[ch]->mStreamTime);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::oscillateGlobalVolume(float aFrom, float aTo, time aTime)
+ {
+ if (aTime <= 0 || aTo == aFrom)
+ {
+ setGlobalVolume(aTo);
+ return;
+ }
+ mGlobalVolumeFader.setLFO(aFrom, aTo, aTime, mStreamTime);
+ }
+}
diff --git a/src/soloud/src/core/soloud_core_filterops.cpp b/src/soloud/src/core/soloud_core_filterops.cpp
new file mode 100644
index 0000000..7a8f264
--- /dev/null
+++ b/src/soloud/src/core/soloud_core_filterops.cpp
@@ -0,0 +1,156 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud_internal.h"
+
+// Core operations related to filters
+
+namespace SoLoud
+{
+ void Soloud::setGlobalFilter(unsigned int aFilterId, Filter *aFilter)
+ {
+ if (aFilterId >= FILTERS_PER_STREAM)
+ return;
+
+ lockAudioMutex();
+ delete mFilterInstance[aFilterId];
+ mFilterInstance[aFilterId] = 0;
+
+ mFilter[aFilterId] = aFilter;
+ if (aFilter)
+ {
+ mFilterInstance[aFilterId] = mFilter[aFilterId]->createInstance();
+ }
+ unlockAudioMutex();
+ }
+
+ float Soloud::getFilterParameter(handle aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId)
+ {
+ float ret = INVALID_PARAMETER;
+ if (aFilterId >= FILTERS_PER_STREAM)
+ return ret;
+
+ if (aVoiceHandle == 0)
+ {
+ lockAudioMutex();
+ if (mFilterInstance[aFilterId])
+ {
+ ret = mFilterInstance[aFilterId]->getFilterParameter(aAttributeId);
+ }
+ unlockAudioMutex();
+ return ret;
+ }
+
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ return ret;
+ }
+ lockAudioMutex();
+ if (mVoice[ch] &&
+ mVoice[ch]->mFilter[aFilterId])
+ {
+ ret = mVoice[ch]->mFilter[aFilterId]->getFilterParameter(aAttributeId);
+ }
+ unlockAudioMutex();
+
+ return ret;
+ }
+
+ void Soloud::setFilterParameter(handle aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aValue)
+ {
+ if (aFilterId >= FILTERS_PER_STREAM)
+ return;
+
+ if (aVoiceHandle == 0)
+ {
+ lockAudioMutex();
+ if (mFilterInstance[aFilterId])
+ {
+ mFilterInstance[aFilterId]->setFilterParameter(aAttributeId, aValue);
+ }
+ unlockAudioMutex();
+ return;
+ }
+
+ FOR_ALL_VOICES_PRE
+ if (mVoice[ch] &&
+ mVoice[ch]->mFilter[aFilterId])
+ {
+ mVoice[ch]->mFilter[aFilterId]->setFilterParameter(aAttributeId, aValue);
+ }
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::fadeFilterParameter(handle aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aTo, double aTime)
+ {
+ if (aFilterId >= FILTERS_PER_STREAM)
+ return;
+
+ if (aVoiceHandle == 0)
+ {
+ lockAudioMutex();
+ if (mFilterInstance[aFilterId])
+ {
+ mFilterInstance[aFilterId]->fadeFilterParameter(aAttributeId, aTo, aTime, mStreamTime);
+ }
+ unlockAudioMutex();
+ return;
+ }
+
+ FOR_ALL_VOICES_PRE
+ if (mVoice[ch] &&
+ mVoice[ch]->mFilter[aFilterId])
+ {
+ mVoice[ch]->mFilter[aFilterId]->fadeFilterParameter(aAttributeId, aTo, aTime, mStreamTime);
+ }
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::oscillateFilterParameter(handle aVoiceHandle, unsigned int aFilterId, unsigned int aAttributeId, float aFrom, float aTo, double aTime)
+ {
+ if (aFilterId >= FILTERS_PER_STREAM)
+ return;
+
+ if (aVoiceHandle == 0)
+ {
+ lockAudioMutex();
+ if (mFilterInstance[aFilterId])
+ {
+ mFilterInstance[aFilterId]->oscillateFilterParameter(aAttributeId, aFrom, aTo, aTime, mStreamTime);
+ }
+ unlockAudioMutex();
+ return;
+ }
+
+ FOR_ALL_VOICES_PRE
+ if (mVoice[ch] &&
+ mVoice[ch]->mFilter[aFilterId])
+ {
+ mVoice[ch]->mFilter[aFilterId]->oscillateFilterParameter(aAttributeId, aFrom, aTo, aTime, mStreamTime);
+ }
+ FOR_ALL_VOICES_POST
+ }
+
+}
diff --git a/src/soloud/src/core/soloud_core_getters.cpp b/src/soloud/src/core/soloud_core_getters.cpp
new file mode 100644
index 0000000..74b93eb
--- /dev/null
+++ b/src/soloud/src/core/soloud_core_getters.cpp
@@ -0,0 +1,378 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+
+// Getters - return information about SoLoud state
+
+namespace SoLoud
+{
+ unsigned int Soloud::getVersion() const
+ {
+ return SOLOUD_VERSION;
+ }
+
+ float Soloud::getPostClipScaler() const
+ {
+ return mPostClipScaler;
+ }
+
+ float Soloud::getGlobalVolume() const
+ {
+ return mGlobalVolume;
+ }
+
+ handle Soloud::getHandleFromVoice(unsigned int aVoice) const
+ {
+ if (mVoice[aVoice] == 0)
+ return 0;
+ return (aVoice + 1) | (mVoice[aVoice]->mPlayIndex << 12);
+ }
+
+ int Soloud::getVoiceFromHandle(handle aVoiceHandle) const
+ {
+ // If this is a voice group handle, pick the first handle from the group
+ handle *h = voiceGroupHandleToArray(aVoiceHandle);
+ if (h != NULL) aVoiceHandle = *h;
+
+ if (aVoiceHandle == 0)
+ {
+ return -1;
+ }
+
+ int ch = (aVoiceHandle & 0xfff) - 1;
+ unsigned int idx = aVoiceHandle >> 12;
+ if (mVoice[ch] &&
+ (mVoice[ch]->mPlayIndex & 0xfffff) == idx)
+ {
+ return ch;
+ }
+ return -1;
+ }
+
+ unsigned int Soloud::getMaxActiveVoiceCount() const
+ {
+ return mMaxActiveVoices;
+ }
+
+ unsigned int Soloud::getActiveVoiceCount()
+ {
+ lockAudioMutex();
+ if (mActiveVoiceDirty)
+ calcActiveVoices();
+ unsigned int c = mActiveVoiceCount;
+ unlockAudioMutex();
+ return c;
+ }
+
+ unsigned int Soloud::getVoiceCount()
+ {
+ lockAudioMutex();
+ int i;
+ int c = 0;
+ for (i = 0; i < (signed)mHighestVoice; i++)
+ {
+ if (mVoice[i])
+ {
+ c++;
+ }
+ }
+ unlockAudioMutex();
+ return c;
+ }
+
+ bool Soloud::isValidVoiceHandle(handle aVoiceHandle)
+ {
+ // voice groups are not valid voice handles
+ if ((aVoiceHandle & 0xfffff000) == 0xfffff000)
+ return 0;
+
+ lockAudioMutex();
+ if (getVoiceFromHandle(aVoiceHandle) != -1)
+ {
+ unlockAudioMutex();
+ return 1;
+ }
+ unlockAudioMutex();
+ return 0;
+ }
+
+
+ time Soloud::getLoopPoint(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ time v = mVoice[ch]->mLoopPoint;
+ unlockAudioMutex();
+ return v;
+ }
+
+ bool Soloud::getLooping(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ bool v = (mVoice[ch]->mFlags & AudioSourceInstance::LOOPING) != 0;
+ unlockAudioMutex();
+ return v;
+ }
+
+ float Soloud::getInfo(handle aVoiceHandle, unsigned int mInfoKey)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ float v = mVoice[ch]->getInfo(mInfoKey);
+ unlockAudioMutex();
+ return v;
+ }
+
+ float Soloud::getVolume(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ float v = mVoice[ch]->mSetVolume;
+ unlockAudioMutex();
+ return v;
+ }
+
+ float Soloud::getOverallVolume(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ float v = mVoice[ch]->mOverallVolume;
+ unlockAudioMutex();
+ return v;
+ }
+
+ float Soloud::getPan(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ float v = mVoice[ch]->mPan;
+ unlockAudioMutex();
+ return v;
+ }
+
+ time Soloud::getStreamTime(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ double v = mVoice[ch]->mStreamTime;
+ unlockAudioMutex();
+ return v;
+ }
+
+ time Soloud::getStreamPosition(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ double v = mVoice[ch]->mStreamPosition;
+ unlockAudioMutex();
+ return v;
+ }
+
+ float Soloud::getRelativePlaySpeed(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 1;
+ }
+ float v = mVoice[ch]->mSetRelativePlaySpeed;
+ unlockAudioMutex();
+ return v;
+ }
+
+ float Soloud::getSamplerate(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ float v = mVoice[ch]->mBaseSamplerate;
+ unlockAudioMutex();
+ return v;
+ }
+
+ bool Soloud::getPause(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ int v = !!(mVoice[ch]->mFlags & AudioSourceInstance::PAUSED);
+ unlockAudioMutex();
+ return v != 0;
+ }
+
+ bool Soloud::getProtectVoice(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ int v = !!(mVoice[ch]->mFlags & AudioSourceInstance::PROTECTED);
+ unlockAudioMutex();
+ return v != 0;
+ }
+
+ int Soloud::findFreeVoice()
+ {
+ int i;
+ unsigned int lowest_play_index_value = 0xffffffff;
+ int lowest_play_index = -1;
+
+ // (slowly) drag the highest active voice index down
+ if (mHighestVoice > 0 && mVoice[mHighestVoice - 1] == NULL)
+ mHighestVoice--;
+
+ for (i = 0; i < VOICE_COUNT; i++)
+ {
+ if (mVoice[i] == NULL)
+ {
+ if (i+1 > (signed)mHighestVoice)
+ {
+ mHighestVoice = i + 1;
+ }
+ return i;
+ }
+ if (((mVoice[i]->mFlags & AudioSourceInstance::PROTECTED) == 0) &&
+ mVoice[i]->mPlayIndex < lowest_play_index_value)
+ {
+ lowest_play_index_value = mVoice[i]->mPlayIndex;
+ lowest_play_index = i;
+ }
+ }
+ stopVoice(lowest_play_index);
+ return lowest_play_index;
+ }
+
+ unsigned int Soloud::getLoopCount(handle aVoiceHandle)
+ {
+ lockAudioMutex();
+ int ch = getVoiceFromHandle(aVoiceHandle);
+ if (ch == -1)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ int v = mVoice[ch]->mLoopCount;
+ unlockAudioMutex();
+ return v;
+ }
+
+ // Returns current backend ID
+ unsigned int Soloud::getBackendId()
+ {
+ return mBackendID;
+
+ }
+
+ // Returns current backend string
+ const char * Soloud::getBackendString()
+ {
+ return mBackendString;
+ }
+
+ // Returns current backend channel count (1 mono, 2 stereo, etc)
+ unsigned int Soloud::getBackendChannels()
+ {
+ return mChannels;
+ }
+
+ // Returns current backend sample rate
+ unsigned int Soloud::getBackendSamplerate()
+ {
+ return mSamplerate;
+ }
+
+ // Returns current backend buffer size
+ unsigned int Soloud::getBackendBufferSize()
+ {
+ return mBufferSize;
+ }
+
+ // Get speaker position in 3d space
+ result Soloud::getSpeakerPosition(unsigned int aChannel, float &aX, float &aY, float &aZ)
+ {
+ if (aChannel >= mChannels)
+ return INVALID_PARAMETER;
+ aX = m3dSpeakerPosition[3 * aChannel + 0];
+ aY = m3dSpeakerPosition[3 * aChannel + 1];
+ aZ = m3dSpeakerPosition[3 * aChannel + 2];
+ return SO_NO_ERROR;
+ }
+
+
+}
diff --git a/src/soloud/src/core/soloud_core_setters.cpp b/src/soloud/src/core/soloud_core_setters.cpp
new file mode 100644
index 0000000..21c20ba
--- /dev/null
+++ b/src/soloud/src/core/soloud_core_setters.cpp
@@ -0,0 +1,223 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud_internal.h"
+
+// Setters - set various bits of SoLoud state
+
+namespace SoLoud
+{
+ void Soloud::setPostClipScaler(float aScaler)
+ {
+ mPostClipScaler = aScaler;
+ }
+
+ void Soloud::setGlobalVolume(float aVolume)
+ {
+ mGlobalVolumeFader.mActive = 0;
+ mGlobalVolume = aVolume;
+ }
+
+ result Soloud::setRelativePlaySpeed(handle aVoiceHandle, float aSpeed)
+ {
+ result retVal = 0;
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mRelativePlaySpeedFader.mActive = 0;
+ retVal = setVoiceRelativePlaySpeed(ch, aSpeed);
+ FOR_ALL_VOICES_POST
+ return retVal;
+ }
+
+ void Soloud::setSamplerate(handle aVoiceHandle, float aSamplerate)
+ {
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mBaseSamplerate = aSamplerate;
+ updateVoiceRelativePlaySpeed(ch);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::setPause(handle aVoiceHandle, bool aPause)
+ {
+ FOR_ALL_VOICES_PRE
+ setVoicePause(ch, aPause);
+ FOR_ALL_VOICES_POST
+ }
+
+ result Soloud::setMaxActiveVoiceCount(unsigned int aVoiceCount)
+ {
+ if (aVoiceCount == 0 || aVoiceCount >= VOICE_COUNT)
+ return INVALID_PARAMETER;
+ lockAudioMutex();
+ mMaxActiveVoices = aVoiceCount;
+ delete[] mResampleData;
+ delete[] mResampleDataOwner;
+ mResampleData = new AlignedFloatBuffer[aVoiceCount * 2];
+ mResampleDataOwner = new AudioSourceInstance*[aVoiceCount];
+ unsigned int i;
+ for (i = 0; i < aVoiceCount * 2; i++)
+ mResampleData[i].init(SAMPLE_GRANULARITY * MAX_CHANNELS);
+ for (i = 0; i < aVoiceCount; i++)
+ mResampleDataOwner[i] = NULL;
+ mActiveVoiceDirty = true;
+ unlockAudioMutex();
+ return SO_NO_ERROR;
+ }
+
+ void Soloud::setPauseAll(bool aPause)
+ {
+ lockAudioMutex();
+ int ch;
+ for (ch = 0; ch < (signed)mHighestVoice; ch++)
+ {
+ setVoicePause(ch, aPause);
+ }
+ unlockAudioMutex();
+ }
+
+ void Soloud::setProtectVoice(handle aVoiceHandle, bool aProtect)
+ {
+ FOR_ALL_VOICES_PRE
+ if (aProtect)
+ {
+ mVoice[ch]->mFlags |= AudioSourceInstance::PROTECTED;
+ }
+ else
+ {
+ mVoice[ch]->mFlags &= ~AudioSourceInstance::PROTECTED;
+ }
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::setPan(handle aVoiceHandle, float aPan)
+ {
+ FOR_ALL_VOICES_PRE
+ setVoicePan(ch, aPan);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::setPanAbsolute(handle aVoiceHandle, float aLVolume, float aRVolume, float aLBVolume, float aRBVolume, float aCVolume, float aSVolume)
+ {
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mPanFader.mActive = 0;
+ mVoice[ch]->mChannelVolume[0] = aLVolume;
+ mVoice[ch]->mChannelVolume[1] = aRVolume;
+ if (mVoice[ch]->mChannels == 4)
+ {
+ mVoice[ch]->mChannelVolume[2] = aLBVolume;
+ mVoice[ch]->mChannelVolume[3] = aRBVolume;
+ }
+ if (mVoice[ch]->mChannels == 6)
+ {
+ mVoice[ch]->mChannelVolume[2] = aCVolume;
+ mVoice[ch]->mChannelVolume[3] = aSVolume;
+ mVoice[ch]->mChannelVolume[4] = aLBVolume;
+ mVoice[ch]->mChannelVolume[5] = aRBVolume;
+ }
+ if (mVoice[ch]->mChannels == 8)
+ {
+ mVoice[ch]->mChannelVolume[2] = aCVolume;
+ mVoice[ch]->mChannelVolume[3] = aSVolume;
+ mVoice[ch]->mChannelVolume[4] = (aLVolume + aLBVolume) * 0.5f;
+ mVoice[ch]->mChannelVolume[5] = (aRVolume + aRBVolume) * 0.5f;
+ mVoice[ch]->mChannelVolume[6] = aLBVolume;
+ mVoice[ch]->mChannelVolume[7] = aRBVolume;
+ }
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::setInaudibleBehavior(handle aVoiceHandle, bool aMustTick, bool aKill)
+ {
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mFlags &= ~(AudioSourceInstance::INAUDIBLE_KILL | AudioSourceInstance::INAUDIBLE_TICK);
+ if (aMustTick)
+ {
+ mVoice[ch]->mFlags |= AudioSourceInstance::INAUDIBLE_TICK;
+ }
+ if (aKill)
+ {
+ mVoice[ch]->mFlags |= AudioSourceInstance::INAUDIBLE_KILL;
+ }
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::setLoopPoint(handle aVoiceHandle, time aLoopPoint)
+ {
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mLoopPoint = aLoopPoint;
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::setLooping(handle aVoiceHandle, bool aLooping)
+ {
+ FOR_ALL_VOICES_PRE
+ if (aLooping)
+ {
+ mVoice[ch]->mFlags |= AudioSourceInstance::LOOPING;
+ }
+ else
+ {
+ mVoice[ch]->mFlags &= ~AudioSourceInstance::LOOPING;
+ }
+ FOR_ALL_VOICES_POST
+ }
+
+
+ void Soloud::setVolume(handle aVoiceHandle, float aVolume)
+ {
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mVolumeFader.mActive = 0;
+ setVoiceVolume(ch, aVolume);
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::setDelaySamples(handle aVoiceHandle, unsigned int aSamples)
+ {
+ FOR_ALL_VOICES_PRE
+ mVoice[ch]->mDelaySamples = aSamples;
+ FOR_ALL_VOICES_POST
+ }
+
+ void Soloud::setVisualizationEnable(bool aEnable)
+ {
+ if (aEnable)
+ {
+ mFlags |= ENABLE_VISUALIZATION;
+ }
+ else
+ {
+ mFlags &= ~ENABLE_VISUALIZATION;
+ }
+ }
+
+ result Soloud::setSpeakerPosition(unsigned int aChannel, float aX, float aY, float aZ)
+ {
+ if (aChannel >= mChannels)
+ return INVALID_PARAMETER;
+ m3dSpeakerPosition[3 * aChannel + 0] = aX;
+ m3dSpeakerPosition[3 * aChannel + 1] = aY;
+ m3dSpeakerPosition[3 * aChannel + 2] = aZ;
+ return SO_NO_ERROR;
+ }
+
+}
diff --git a/src/soloud/src/core/soloud_core_voicegroup.cpp b/src/soloud/src/core/soloud_core_voicegroup.cpp
new file mode 100644
index 0000000..7a3a571
--- /dev/null
+++ b/src/soloud/src/core/soloud_core_voicegroup.cpp
@@ -0,0 +1,255 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+
+// Voice group operations
+
+namespace SoLoud
+{
+ // Create a voice group. Returns 0 if unable (out of voice groups / out of memory)
+ handle Soloud::createVoiceGroup()
+ {
+ lockAudioMutex();
+
+ unsigned int i;
+ // Check if there's any deleted voice groups and re-use if found
+ for (i = 0; i < mVoiceGroupCount; i++)
+ {
+ if (mVoiceGroup[i] == NULL)
+ {
+ mVoiceGroup[i] = new unsigned int[16];
+ if (mVoiceGroup[i] == NULL)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ mVoiceGroup[i][0] = 16;
+ mVoiceGroup[i][1] = 0;
+ unlockAudioMutex();
+ return 0xfffff000 | i;
+ }
+ }
+ if (mVoiceGroupCount == 4096)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ unsigned int oldcount = mVoiceGroupCount;
+ if (mVoiceGroupCount == 0)
+ {
+ mVoiceGroupCount = 4;
+ }
+ mVoiceGroupCount *= 2;
+ unsigned int **vg = new unsigned int * [mVoiceGroupCount];
+ if (vg == NULL)
+ {
+ mVoiceGroupCount = oldcount;
+ unlockAudioMutex();
+ return 0;
+ }
+ for (i = 0; i < oldcount; i++)
+ {
+ vg[i] = mVoiceGroup[i];
+ }
+
+ for (; i < mVoiceGroupCount; i++)
+ {
+ vg[i] = NULL;
+ }
+
+ delete[] mVoiceGroup;
+ mVoiceGroup = vg;
+ i = oldcount;
+ mVoiceGroup[i] = new unsigned int[17];
+ if (mVoiceGroup[i] == NULL)
+ {
+ unlockAudioMutex();
+ return 0;
+ }
+ mVoiceGroup[i][0] = 16;
+ mVoiceGroup[i][1] = 0;
+ unlockAudioMutex();
+ return 0xfffff000 | i;
+ }
+
+ // Destroy a voice group.
+ result Soloud::destroyVoiceGroup(handle aVoiceGroupHandle)
+ {
+ if (!isVoiceGroup(aVoiceGroupHandle))
+ return INVALID_PARAMETER;
+ int c = aVoiceGroupHandle & 0xfff;
+
+ lockAudioMutex();
+ delete[] mVoiceGroup[c];
+ mVoiceGroup[c] = NULL;
+ unlockAudioMutex();
+ return SO_NO_ERROR;
+ }
+
+ // Add a voice handle to a voice group
+ result Soloud::addVoiceToGroup(handle aVoiceGroupHandle, handle aVoiceHandle)
+ {
+ if (!isVoiceGroup(aVoiceGroupHandle))
+ return INVALID_PARAMETER;
+
+ // Don't consider adding invalid voice handles as an error, since the voice may just have ended.
+ if (!isValidVoiceHandle(aVoiceHandle))
+ return SO_NO_ERROR;
+
+ trimVoiceGroup(aVoiceGroupHandle);
+
+ int c = aVoiceGroupHandle & 0xfff;
+ unsigned int i;
+
+ lockAudioMutex();
+
+ for (i = 1; i < mVoiceGroup[c][0]; i++)
+ {
+ if (mVoiceGroup[c][i] == aVoiceHandle)
+ {
+ unlockAudioMutex();
+ return SO_NO_ERROR; // already there
+ }
+
+ if (mVoiceGroup[c][i] == 0)
+ {
+ mVoiceGroup[c][i] = aVoiceHandle;
+ mVoiceGroup[c][i + 1] = 0;
+
+ unlockAudioMutex();
+ return SO_NO_ERROR;
+ }
+ }
+
+ // Full group, allocate more memory
+ unsigned int * n = new unsigned int[mVoiceGroup[c][0] * 2 + 1];
+ if (n == NULL)
+ {
+ unlockAudioMutex();
+ return OUT_OF_MEMORY;
+ }
+ for (i = 0; i < mVoiceGroup[c][0]; i++)
+ n[i] = mVoiceGroup[c][i];
+ n[n[0]] = aVoiceHandle;
+ n[n[0]+1] = 0;
+ n[0] *= 2;
+ delete[] mVoiceGroup[c];
+ mVoiceGroup[c] = n;
+ unlockAudioMutex();
+ return SO_NO_ERROR;
+ }
+
+ // Is this handle a valid voice group?
+ bool Soloud::isVoiceGroup(handle aVoiceGroupHandle)
+ {
+ if ((aVoiceGroupHandle & 0xfffff000) != 0xfffff000)
+ return 0;
+ unsigned int c = aVoiceGroupHandle & 0xfff;
+ if (c >= mVoiceGroupCount)
+ return 0;
+
+ lockAudioMutex();
+ bool res = mVoiceGroup[c] != NULL;
+ unlockAudioMutex();
+
+ return res;
+ }
+
+ // Is this voice group empty?
+ bool Soloud::isVoiceGroupEmpty(handle aVoiceGroupHandle)
+ {
+ // If not a voice group, yeah, we're empty alright..
+ if (!isVoiceGroup(aVoiceGroupHandle))
+ return 1;
+ trimVoiceGroup(aVoiceGroupHandle);
+ int c = aVoiceGroupHandle & 0xfff;
+
+ lockAudioMutex();
+ bool res = mVoiceGroup[c][1] == 0;
+ unlockAudioMutex();
+
+ return res;
+ }
+
+ // Remove all non-active voices from group
+ void Soloud::trimVoiceGroup(handle aVoiceGroupHandle)
+ {
+ if (!isVoiceGroup(aVoiceGroupHandle))
+ return;
+ int c = aVoiceGroupHandle & 0xfff;
+
+ lockAudioMutex();
+ // empty group
+ if (mVoiceGroup[c][1] == 0)
+ {
+ unlockAudioMutex();
+ return;
+ }
+
+ unsigned int i;
+ for (i = 1; i < mVoiceGroup[c][0]; i++)
+ {
+ if (mVoiceGroup[c][i] == 0)
+ {
+ unlockAudioMutex();
+ return;
+ }
+
+ unlockAudioMutex();
+ while (!isValidVoiceHandle(mVoiceGroup[c][i])) // function locks mutex, so we need to unlock it before the call
+ {
+ lockAudioMutex();
+ unsigned int j;
+ for (j = i; j < mVoiceGroup[c][0] - 1; j++)
+ {
+ mVoiceGroup[c][j] = mVoiceGroup[c][j + 1];
+ if (mVoiceGroup[c][j] == 0)
+ break;
+ }
+ mVoiceGroup[c][mVoiceGroup[c][0] - 1] = 0;
+ if (mVoiceGroup[c][i] == 0)
+ {
+ unlockAudioMutex();
+ return;
+ }
+ }
+ lockAudioMutex();
+ }
+ unlockAudioMutex();
+ }
+
+ handle *Soloud::voiceGroupHandleToArray(handle aVoiceGroupHandle) const
+ {
+ if ((aVoiceGroupHandle & 0xfffff000) != 0xfffff000)
+ return NULL;
+ unsigned int c = aVoiceGroupHandle & 0xfff;
+ if (c >= mVoiceGroupCount)
+ return NULL;
+ if (mVoiceGroup[c] == NULL)
+ return NULL;
+ return mVoiceGroup[c] + 1;
+ }
+
+}
diff --git a/src/soloud/src/core/soloud_core_voiceops.cpp b/src/soloud/src/core/soloud_core_voiceops.cpp
new file mode 100644
index 0000000..27a66b5
--- /dev/null
+++ b/src/soloud/src/core/soloud_core_voiceops.cpp
@@ -0,0 +1,153 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+
+// Direct voice operations (no mutexes - called from other functions)
+
+namespace SoLoud
+{
+ result Soloud::setVoiceRelativePlaySpeed(unsigned int aVoice, float aSpeed)
+ {
+ SOLOUD_ASSERT(aVoice < VOICE_COUNT);
+ SOLOUD_ASSERT(mInsideAudioThreadMutex);
+ if (aSpeed <= 0.0f)
+ {
+ return INVALID_PARAMETER;
+ }
+
+ if (mVoice[aVoice])
+ {
+ mVoice[aVoice]->mSetRelativePlaySpeed = aSpeed;
+ updateVoiceRelativePlaySpeed(aVoice);
+ }
+
+ return 0;
+ }
+
+ void Soloud::setVoicePause(unsigned int aVoice, int aPause)
+ {
+ SOLOUD_ASSERT(aVoice < VOICE_COUNT);
+ SOLOUD_ASSERT(mInsideAudioThreadMutex);
+ mActiveVoiceDirty = true;
+ if (mVoice[aVoice])
+ {
+ mVoice[aVoice]->mPauseScheduler.mActive = 0;
+
+ if (aPause)
+ {
+ mVoice[aVoice]->mFlags |= AudioSourceInstance::PAUSED;
+ }
+ else
+ {
+ mVoice[aVoice]->mFlags &= ~AudioSourceInstance::PAUSED;
+ }
+ }
+ }
+
+ void Soloud::setVoicePan(unsigned int aVoice, float aPan)
+ {
+ SOLOUD_ASSERT(aVoice < VOICE_COUNT);
+ SOLOUD_ASSERT(mInsideAudioThreadMutex);
+ if (mVoice[aVoice])
+ {
+ mVoice[aVoice]->mPan = aPan;
+ float l = (float)cos((aPan + 1) * M_PI / 4);
+ float r = (float)sin((aPan + 1) * M_PI / 4);
+ mVoice[aVoice]->mChannelVolume[0] = l;
+ mVoice[aVoice]->mChannelVolume[1] = r;
+ if (mVoice[aVoice]->mChannels == 4)
+ {
+ mVoice[aVoice]->mChannelVolume[2] = l;
+ mVoice[aVoice]->mChannelVolume[3] = r;
+ }
+ if (mVoice[aVoice]->mChannels == 6)
+ {
+ mVoice[aVoice]->mChannelVolume[2] = 1.0f / (float)sqrt(2.0f);
+ mVoice[aVoice]->mChannelVolume[3] = 1;
+ mVoice[aVoice]->mChannelVolume[4] = l;
+ mVoice[aVoice]->mChannelVolume[5] = r;
+ }
+ if (mVoice[aVoice]->mChannels == 8)
+ {
+ mVoice[aVoice]->mChannelVolume[2] = 1.0f / (float)sqrt(2.0f);
+ mVoice[aVoice]->mChannelVolume[3] = 1;
+ mVoice[aVoice]->mChannelVolume[4] = l;
+ mVoice[aVoice]->mChannelVolume[5] = r;
+ mVoice[aVoice]->mChannelVolume[6] = l;
+ mVoice[aVoice]->mChannelVolume[7] = r;
+ }
+ }
+ }
+
+ void Soloud::setVoiceVolume(unsigned int aVoice, float aVolume)
+ {
+ SOLOUD_ASSERT(aVoice < VOICE_COUNT);
+ SOLOUD_ASSERT(mInsideAudioThreadMutex);
+ mActiveVoiceDirty = true;
+ if (mVoice[aVoice])
+ {
+ mVoice[aVoice]->mSetVolume = aVolume;
+ updateVoiceVolume(aVoice);
+ }
+ }
+
+ void Soloud::stopVoice(unsigned int aVoice)
+ {
+ SOLOUD_ASSERT(aVoice < VOICE_COUNT);
+ SOLOUD_ASSERT(mInsideAudioThreadMutex);
+ mActiveVoiceDirty = true;
+ if (mVoice[aVoice])
+ {
+ // Delete via temporary variable to avoid recursion
+ AudioSourceInstance * v = mVoice[aVoice];
+ mVoice[aVoice] = 0;
+ delete v;
+
+ unsigned int i;
+ for (i = 0; i < mMaxActiveVoices; i++)
+ {
+ if (mResampleDataOwner[i] == v)
+ {
+ mResampleDataOwner[i] = NULL;
+ }
+ }
+ }
+ }
+
+ void Soloud::updateVoiceRelativePlaySpeed(unsigned int aVoice)
+ {
+ SOLOUD_ASSERT(aVoice < VOICE_COUNT);
+ SOLOUD_ASSERT(mInsideAudioThreadMutex);
+ mVoice[aVoice]->mOverallRelativePlaySpeed = m3dData[aVoice].mDopplerValue * mVoice[aVoice]->mSetRelativePlaySpeed;
+ mVoice[aVoice]->mSamplerate = mVoice[aVoice]->mBaseSamplerate * mVoice[aVoice]->mOverallRelativePlaySpeed;
+ }
+
+ void Soloud::updateVoiceVolume(unsigned int aVoice)
+ {
+ SOLOUD_ASSERT(aVoice < VOICE_COUNT);
+ SOLOUD_ASSERT(mInsideAudioThreadMutex);
+ mVoice[aVoice]->mOverallVolume = mVoice[aVoice]->mSetVolume * m3dData[aVoice].m3dVolume;
+ }
+}
diff --git a/src/soloud/src/core/soloud_fader.cpp b/src/soloud/src/core/soloud_fader.cpp
new file mode 100644
index 0000000..79f1278
--- /dev/null
+++ b/src/soloud/src/core/soloud_fader.cpp
@@ -0,0 +1,94 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ Fader::Fader()
+ {
+ mCurrent = mFrom = mTo = mDelta = 0;
+ mTime = mStartTime = mEndTime = 0;
+ mActive = 0;
+ }
+
+ void Fader::set(float aFrom, float aTo, double aTime, double aStartTime)
+ {
+ mCurrent = mFrom;
+ mFrom = aFrom;
+ mTo = aTo;
+ mTime = aTime;
+ mStartTime = aStartTime;
+ mDelta = aTo - aFrom;
+ mEndTime = mStartTime + mTime;
+ mActive = 1;
+ }
+
+ void Fader::setLFO(float aFrom, float aTo, double aTime, double aStartTime)
+ {
+ mActive = 2;
+ mCurrent = 0;
+ mFrom = aFrom;
+ mTo = aTo;
+ mTime = aTime;
+ mDelta = (aTo - aFrom) / 2;
+ if (mDelta < 0) mDelta = -mDelta;
+ mStartTime = aStartTime;
+ mEndTime = (float)M_PI * 2 / mTime;
+ }
+
+ float Fader::get(double aCurrentTime)
+ {
+ if (mActive == 2)
+ {
+ // LFO mode
+ if (mStartTime > aCurrentTime)
+ {
+ // Time rolled over.
+ mStartTime = aCurrentTime;
+ }
+ double t = aCurrentTime - mStartTime;
+ return (float)(sin(t * mEndTime) * mDelta + (mFrom + mDelta));
+
+ }
+ if (mStartTime > aCurrentTime)
+ {
+ // Time rolled over.
+ // Figure out where we were..
+ float p = (mCurrent - mFrom) / mDelta; // 0..1
+ mFrom = mCurrent;
+ mStartTime = aCurrentTime;
+ mTime = mTime * (1 - p); // time left
+ mDelta = mTo - mFrom;
+ mEndTime = mStartTime + mTime;
+ }
+ if (aCurrentTime > mEndTime)
+ {
+ mActive = -1;
+ return mTo;
+ }
+ mCurrent = (float)(mFrom + mDelta * ((aCurrentTime - mStartTime) / mTime));
+ return mCurrent;
+ }
+};
diff --git a/src/soloud/src/core/soloud_fft.cpp b/src/soloud/src/core/soloud_fft.cpp
new file mode 100644
index 0000000..fb87be1
--- /dev/null
+++ b/src/soloud/src/core/soloud_fft.cpp
@@ -0,0 +1,2474 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+// FFT based on fftreal by Laurent de Soras, under WTFPL
+
+#include "soloud.h"
+#include "soloud_fft.h"
+#include
+
+namespace fftimpl
+{
+// Based on http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html
+// "Copyright Takuya OOURA, 1996-2001
+// You may use, copy, modify and distribute this code for any purpose
+// (include commercial use) and without fee. Please refer to this package
+// when you modify this code."
+
+
+#ifndef M_PI_2
+#define M_PI_2 1.570796326794896619231321691639751442098584699687f
+#endif
+#ifndef WR5000 /* cos(M_PI_2*0.5000) */
+#define WR5000 0.707106781186547524400844362104849039284835937688f
+#endif
+#ifndef WR2500 /* cos(M_PI_2*0.2500) */
+#define WR2500 0.923879532511286756128183189396788286822416625863f
+#endif
+#ifndef WI2500 /* sin(M_PI_2*0.2500) */
+#define WI2500 0.382683432365089771728459984030398866761344562485f
+#endif
+#ifndef WR1250 /* cos(M_PI_2*0.1250) */
+#define WR1250 0.980785280403230449126182236134239036973933730893f
+#endif
+#ifndef WI1250 /* sin(M_PI_2*0.1250) */
+#define WI1250 0.195090322016128267848284868477022240927691617751f
+#endif
+#ifndef WR3750 /* cos(M_PI_2*0.3750) */
+#define WR3750 0.831469612302545237078788377617905756738560811987f
+#endif
+#ifndef WI3750 /* sin(M_PI_2*0.3750) */
+#define WI3750 0.555570233019602224742830813948532874374937190754f
+#endif
+
+#ifndef CDFT_LOOP_DIV /* control of the CDFT's speed & tolerance */
+#define CDFT_LOOP_DIV 32
+#endif
+
+#ifndef RDFT_LOOP_DIV /* control of the RDFT's speed & tolerance */
+#define RDFT_LOOP_DIV 64
+#endif
+
+#ifndef DCST_LOOP_DIV /* control of the DCT,DST's speed & tolerance */
+#define DCST_LOOP_DIV 64
+#endif
+
+ void bitrv2(int n, float *a)
+ {
+ int j0, k0, j1, k1, l, m, i, j, k, nh;
+ float xr, xi, yr, yi;
+
+ m = 4;
+ for (l = n >> 2; l > 8; l >>= 2) {
+ m <<= 1;
+ }
+ nh = n >> 1;
+ if (l == 8) {
+ j0 = 0;
+ for (k0 = 0; k0 < m; k0 += 4) {
+ k = k0;
+ for (j = j0; j < j0 + k0; j += 4) {
+ xr = a[j];
+ xi = a[j + 1];
+ yr = a[k];
+ yi = a[k + 1];
+ a[j] = yr;
+ a[j + 1] = yi;
+ a[k] = xr;
+ a[k + 1] = xi;
+ j1 = j + m;
+ k1 = k + 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 -= m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += nh;
+ k1 += 2;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 += m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += 2;
+ k1 += nh;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 -= m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= nh;
+ k1 -= 2;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 += m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ for (i = nh >> 1; i > (k ^= i); i >>= 1);
+ }
+ k1 = j0 + k0;
+ j1 = k1 + 2;
+ k1 += nh;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += 2 * m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 -= m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= 2;
+ k1 -= nh;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += nh + 2;
+ k1 += nh + 2;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= nh - m;
+ k1 += 2 * m - 2;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ for (i = nh >> 1; i > (j0 ^= i); i >>= 1);
+ }
+ }
+ else {
+ j0 = 0;
+ for (k0 = 0; k0 < m; k0 += 4) {
+ k = k0;
+ for (j = j0; j < j0 + k0; j += 4) {
+ xr = a[j];
+ xi = a[j + 1];
+ yr = a[k];
+ yi = a[k + 1];
+ a[j] = yr;
+ a[j + 1] = yi;
+ a[k] = xr;
+ a[k + 1] = xi;
+ j1 = j + m;
+ k1 = k + m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += nh;
+ k1 += 2;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += 2;
+ k1 += nh;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= nh;
+ k1 -= 2;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ for (i = nh >> 1; i > (k ^= i); i >>= 1);
+ }
+ k1 = j0 + k0;
+ j1 = k1 + 2;
+ k1 += nh;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += m;
+ xr = a[j1];
+ xi = a[j1 + 1];
+ yr = a[k1];
+ yi = a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ for (i = nh >> 1; i > (j0 ^= i); i >>= 1);
+ }
+ }
+ }
+
+
+ void bitrv2conj(int n, float *a)
+ {
+ int j0, k0, j1, k1, l, m, i, j, k, nh;
+ float xr, xi, yr, yi;
+
+ m = 4;
+ for (l = n >> 2; l > 8; l >>= 2) {
+ m <<= 1;
+ }
+ nh = n >> 1;
+ if (l == 8) {
+ j0 = 0;
+ for (k0 = 0; k0 < m; k0 += 4) {
+ k = k0;
+ for (j = j0; j < j0 + k0; j += 4) {
+ xr = a[j];
+ xi = -a[j + 1];
+ yr = a[k];
+ yi = -a[k + 1];
+ a[j] = yr;
+ a[j + 1] = yi;
+ a[k] = xr;
+ a[k + 1] = xi;
+ j1 = j + m;
+ k1 = k + 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 -= m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += nh;
+ k1 += 2;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 += m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += 2;
+ k1 += nh;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 -= m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= nh;
+ k1 -= 2;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 += m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ for (i = nh >> 1; i > (k ^= i); i >>= 1);
+ }
+ k1 = j0 + k0;
+ j1 = k1 + 2;
+ k1 += nh;
+ a[j1 - 1] = -a[j1 - 1];
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ a[k1 + 3] = -a[k1 + 3];
+ j1 += m;
+ k1 += 2 * m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 -= m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= 2;
+ k1 -= nh;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += nh + 2;
+ k1 += nh + 2;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= nh - m;
+ k1 += 2 * m - 2;
+ a[j1 - 1] = -a[j1 - 1];
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ a[k1 + 3] = -a[k1 + 3];
+ for (i = nh >> 1; i > (j0 ^= i); i >>= 1);
+ }
+ }
+ else {
+ j0 = 0;
+ for (k0 = 0; k0 < m; k0 += 4) {
+ k = k0;
+ for (j = j0; j < j0 + k0; j += 4) {
+ xr = a[j];
+ xi = -a[j + 1];
+ yr = a[k];
+ yi = -a[k + 1];
+ a[j] = yr;
+ a[j + 1] = yi;
+ a[k] = xr;
+ a[k + 1] = xi;
+ j1 = j + m;
+ k1 = k + m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += nh;
+ k1 += 2;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += 2;
+ k1 += nh;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 += m;
+ k1 += m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= nh;
+ k1 -= 2;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ j1 -= m;
+ k1 -= m;
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ for (i = nh >> 1; i > (k ^= i); i >>= 1);
+ }
+ k1 = j0 + k0;
+ j1 = k1 + 2;
+ k1 += nh;
+ a[j1 - 1] = -a[j1 - 1];
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ a[k1 + 3] = -a[k1 + 3];
+ j1 += m;
+ k1 += m;
+ a[j1 - 1] = -a[j1 - 1];
+ xr = a[j1];
+ xi = -a[j1 + 1];
+ yr = a[k1];
+ yi = -a[k1 + 1];
+ a[j1] = yr;
+ a[j1 + 1] = yi;
+ a[k1] = xr;
+ a[k1 + 1] = xi;
+ a[k1 + 3] = -a[k1 + 3];
+ for (i = nh >> 1; i > (j0 ^= i); i >>= 1);
+ }
+ }
+ }
+
+
+ void bitrv216(float *a)
+ {
+ float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,
+ x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i,
+ x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i;
+
+ x1r = a[2];
+ x1i = a[3];
+ x2r = a[4];
+ x2i = a[5];
+ x3r = a[6];
+ x3i = a[7];
+ x4r = a[8];
+ x4i = a[9];
+ x5r = a[10];
+ x5i = a[11];
+ x7r = a[14];
+ x7i = a[15];
+ x8r = a[16];
+ x8i = a[17];
+ x10r = a[20];
+ x10i = a[21];
+ x11r = a[22];
+ x11i = a[23];
+ x12r = a[24];
+ x12i = a[25];
+ x13r = a[26];
+ x13i = a[27];
+ x14r = a[28];
+ x14i = a[29];
+ a[2] = x8r;
+ a[3] = x8i;
+ a[4] = x4r;
+ a[5] = x4i;
+ a[6] = x12r;
+ a[7] = x12i;
+ a[8] = x2r;
+ a[9] = x2i;
+ a[10] = x10r;
+ a[11] = x10i;
+ a[14] = x14r;
+ a[15] = x14i;
+ a[16] = x1r;
+ a[17] = x1i;
+ a[20] = x5r;
+ a[21] = x5i;
+ a[22] = x13r;
+ a[23] = x13i;
+ a[24] = x3r;
+ a[25] = x3i;
+ a[26] = x11r;
+ a[27] = x11i;
+ a[28] = x7r;
+ a[29] = x7i;
+ }
+
+
+ void bitrv216neg(float *a)
+ {
+ float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,
+ x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i,
+ x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i,
+ x13r, x13i, x14r, x14i, x15r, x15i;
+
+ x1r = a[2];
+ x1i = a[3];
+ x2r = a[4];
+ x2i = a[5];
+ x3r = a[6];
+ x3i = a[7];
+ x4r = a[8];
+ x4i = a[9];
+ x5r = a[10];
+ x5i = a[11];
+ x6r = a[12];
+ x6i = a[13];
+ x7r = a[14];
+ x7i = a[15];
+ x8r = a[16];
+ x8i = a[17];
+ x9r = a[18];
+ x9i = a[19];
+ x10r = a[20];
+ x10i = a[21];
+ x11r = a[22];
+ x11i = a[23];
+ x12r = a[24];
+ x12i = a[25];
+ x13r = a[26];
+ x13i = a[27];
+ x14r = a[28];
+ x14i = a[29];
+ x15r = a[30];
+ x15i = a[31];
+ a[2] = x15r;
+ a[3] = x15i;
+ a[4] = x7r;
+ a[5] = x7i;
+ a[6] = x11r;
+ a[7] = x11i;
+ a[8] = x3r;
+ a[9] = x3i;
+ a[10] = x13r;
+ a[11] = x13i;
+ a[12] = x5r;
+ a[13] = x5i;
+ a[14] = x9r;
+ a[15] = x9i;
+ a[16] = x1r;
+ a[17] = x1i;
+ a[18] = x14r;
+ a[19] = x14i;
+ a[20] = x6r;
+ a[21] = x6i;
+ a[22] = x10r;
+ a[23] = x10i;
+ a[24] = x2r;
+ a[25] = x2i;
+ a[26] = x12r;
+ a[27] = x12i;
+ a[28] = x4r;
+ a[29] = x4i;
+ a[30] = x8r;
+ a[31] = x8i;
+ }
+
+
+ void bitrv208(float *a)
+ {
+ float x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i;
+
+ x1r = a[2];
+ x1i = a[3];
+ x3r = a[6];
+ x3i = a[7];
+ x4r = a[8];
+ x4i = a[9];
+ x6r = a[12];
+ x6i = a[13];
+ a[2] = x4r;
+ a[3] = x4i;
+ a[6] = x6r;
+ a[7] = x6i;
+ a[8] = x1r;
+ a[9] = x1i;
+ a[12] = x3r;
+ a[13] = x3i;
+ }
+
+
+ void bitrv208neg(float *a)
+ {
+ float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i,
+ x5r, x5i, x6r, x6i, x7r, x7i;
+
+ x1r = a[2];
+ x1i = a[3];
+ x2r = a[4];
+ x2i = a[5];
+ x3r = a[6];
+ x3i = a[7];
+ x4r = a[8];
+ x4i = a[9];
+ x5r = a[10];
+ x5i = a[11];
+ x6r = a[12];
+ x6i = a[13];
+ x7r = a[14];
+ x7i = a[15];
+ a[2] = x7r;
+ a[3] = x7i;
+ a[4] = x3r;
+ a[5] = x3i;
+ a[6] = x5r;
+ a[7] = x5i;
+ a[8] = x1r;
+ a[9] = x1i;
+ a[10] = x6r;
+ a[11] = x6i;
+ a[12] = x2r;
+ a[13] = x2i;
+ a[14] = x4r;
+ a[15] = x4i;
+ }
+
+ void cftb1st(int n, float *a)
+ {
+ int i, i0, j, j0, j1, j2, j3, m, mh;
+ float ew, w1r, w1i, wk1r, wk1i, wk3r, wk3i,
+ wd1r, wd1i, wd3r, wd3i, ss1, ss3;
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+ mh = n >> 3;
+ m = 2 * mh;
+ j1 = m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[0] + a[j2];
+ x0i = -a[1] - a[j2 + 1];
+ x1r = a[0] - a[j2];
+ x1i = -a[1] + a[j2 + 1];
+ x2r = a[j1] + a[j3];
+ x2i = a[j1 + 1] + a[j3 + 1];
+ x3r = a[j1] - a[j3];
+ x3i = a[j1 + 1] - a[j3 + 1];
+ a[0] = x0r + x2r;
+ a[1] = x0i - x2i;
+ a[j1] = x0r - x2r;
+ a[j1 + 1] = x0i + x2i;
+ a[j2] = x1r + x3i;
+ a[j2 + 1] = x1i + x3r;
+ a[j3] = x1r - x3i;
+ a[j3 + 1] = x1i - x3r;
+ wd1r = 1;
+ wd1i = 0;
+ wd3r = 1;
+ wd3i = 0;
+ ew = M_PI_2 / m;
+ w1r = (float)cos(2 * ew);
+ w1i = (float)sin(2 * ew);
+ wk1r = w1r;
+ wk1i = w1i;
+ ss1 = 2 * w1i;
+ wk3i = 2 * ss1 * wk1r;
+ wk3r = wk1r - wk3i * wk1i;
+ wk3i = wk1i - wk3i * wk1r;
+ ss3 = 2 * wk3i;
+ i = 0;
+ for (;;) {
+ i0 = i + 4 * CDFT_LOOP_DIV;
+ if (i0 > mh - 4) {
+ i0 = mh - 4;
+ }
+ for (j = i + 2; j < i0; j += 4) {
+ wd1r -= ss1 * wk1i;
+ wd1i += ss1 * wk1r;
+ wd3r -= ss3 * wk3i;
+ wd3i += ss3 * wk3r;
+ j1 = j + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j] + a[j2];
+ x0i = -a[j + 1] - a[j2 + 1];
+ x1r = a[j] - a[j2];
+ x1i = -a[j + 1] + a[j2 + 1];
+ x2r = a[j1] + a[j3];
+ x2i = a[j1 + 1] + a[j3 + 1];
+ x3r = a[j1] - a[j3];
+ x3i = a[j1 + 1] - a[j3 + 1];
+ a[j] = x0r + x2r;
+ a[j + 1] = x0i - x2i;
+ a[j1] = x0r - x2r;
+ a[j1 + 1] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[j2] = wk1r * x0r - wk1i * x0i;
+ a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[j3] = wk3r * x0r + wk3i * x0i;
+ a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+ x0r = a[j + 2] + a[j2 + 2];
+ x0i = -a[j + 3] - a[j2 + 3];
+ x1r = a[j + 2] - a[j2 + 2];
+ x1i = -a[j + 3] + a[j2 + 3];
+ x2r = a[j1 + 2] + a[j3 + 2];
+ x2i = a[j1 + 3] + a[j3 + 3];
+ x3r = a[j1 + 2] - a[j3 + 2];
+ x3i = a[j1 + 3] - a[j3 + 3];
+ a[j + 2] = x0r + x2r;
+ a[j + 3] = x0i - x2i;
+ a[j1 + 2] = x0r - x2r;
+ a[j1 + 3] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[j2 + 2] = wd1r * x0r - wd1i * x0i;
+ a[j2 + 3] = wd1r * x0i + wd1i * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[j3 + 2] = wd3r * x0r + wd3i * x0i;
+ a[j3 + 3] = wd3r * x0i - wd3i * x0r;
+ j0 = m - j;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j0] + a[j2];
+ x0i = -a[j0 + 1] - a[j2 + 1];
+ x1r = a[j0] - a[j2];
+ x1i = -a[j0 + 1] + a[j2 + 1];
+ x2r = a[j1] + a[j3];
+ x2i = a[j1 + 1] + a[j3 + 1];
+ x3r = a[j1] - a[j3];
+ x3i = a[j1 + 1] - a[j3 + 1];
+ a[j0] = x0r + x2r;
+ a[j0 + 1] = x0i - x2i;
+ a[j1] = x0r - x2r;
+ a[j1 + 1] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[j2] = wk1i * x0r - wk1r * x0i;
+ a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[j3] = wk3i * x0r + wk3r * x0i;
+ a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+ x0r = a[j0 - 2] + a[j2 - 2];
+ x0i = -a[j0 - 1] - a[j2 - 1];
+ x1r = a[j0 - 2] - a[j2 - 2];
+ x1i = -a[j0 - 1] + a[j2 - 1];
+ x2r = a[j1 - 2] + a[j3 - 2];
+ x2i = a[j1 - 1] + a[j3 - 1];
+ x3r = a[j1 - 2] - a[j3 - 2];
+ x3i = a[j1 - 1] - a[j3 - 1];
+ a[j0 - 2] = x0r + x2r;
+ a[j0 - 1] = x0i - x2i;
+ a[j1 - 2] = x0r - x2r;
+ a[j1 - 1] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[j2 - 2] = wd1i * x0r - wd1r * x0i;
+ a[j2 - 1] = wd1i * x0i + wd1r * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[j3 - 2] = wd3i * x0r + wd3r * x0i;
+ a[j3 - 1] = wd3i * x0i - wd3r * x0r;
+ wk1r -= ss1 * wd1i;
+ wk1i += ss1 * wd1r;
+ wk3r -= ss3 * wd3i;
+ wk3i += ss3 * wd3r;
+ }
+ if (i0 == mh - 4) {
+ break;
+ }
+ wd1r = (float)cos(ew * i0);
+ wd1i = (float)sin(ew * i0);
+ wd3i = 4 * wd1i * wd1r;
+ wd3r = wd1r - wd3i * wd1i;
+ wd3i = wd1i - wd3i * wd1r;
+ wk1r = w1r * wd1r - w1i * wd1i;
+ wk1i = w1r * wd1i + w1i * wd1r;
+ wk3i = 4 * wk1i * wk1r;
+ wk3r = wk1r - wk3i * wk1i;
+ wk3i = wk1i - wk3i * wk1r;
+ i = i0;
+ }
+ wd1r = WR5000;
+ j0 = mh;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j0 - 2] + a[j2 - 2];
+ x0i = -a[j0 - 1] - a[j2 - 1];
+ x1r = a[j0 - 2] - a[j2 - 2];
+ x1i = -a[j0 - 1] + a[j2 - 1];
+ x2r = a[j1 - 2] + a[j3 - 2];
+ x2i = a[j1 - 1] + a[j3 - 1];
+ x3r = a[j1 - 2] - a[j3 - 2];
+ x3i = a[j1 - 1] - a[j3 - 1];
+ a[j0 - 2] = x0r + x2r;
+ a[j0 - 1] = x0i - x2i;
+ a[j1 - 2] = x0r - x2r;
+ a[j1 - 1] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[j2 - 2] = wk1r * x0r - wk1i * x0i;
+ a[j2 - 1] = wk1r * x0i + wk1i * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[j3 - 2] = wk3r * x0r + wk3i * x0i;
+ a[j3 - 1] = wk3r * x0i - wk3i * x0r;
+ x0r = a[j0] + a[j2];
+ x0i = -a[j0 + 1] - a[j2 + 1];
+ x1r = a[j0] - a[j2];
+ x1i = -a[j0 + 1] + a[j2 + 1];
+ x2r = a[j1] + a[j3];
+ x2i = a[j1 + 1] + a[j3 + 1];
+ x3r = a[j1] - a[j3];
+ x3i = a[j1 + 1] - a[j3 + 1];
+ a[j0] = x0r + x2r;
+ a[j0 + 1] = x0i - x2i;
+ a[j1] = x0r - x2r;
+ a[j1 + 1] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[j2] = wd1r * (x0r - x0i);
+ a[j2 + 1] = wd1r * (x0i + x0r);
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[j3] = -wd1r * (x0r + x0i);
+ a[j3 + 1] = -wd1r * (x0i - x0r);
+ x0r = a[j0 + 2] + a[j2 + 2];
+ x0i = -a[j0 + 3] - a[j2 + 3];
+ x1r = a[j0 + 2] - a[j2 + 2];
+ x1i = -a[j0 + 3] + a[j2 + 3];
+ x2r = a[j1 + 2] + a[j3 + 2];
+ x2i = a[j1 + 3] + a[j3 + 3];
+ x3r = a[j1 + 2] - a[j3 + 2];
+ x3i = a[j1 + 3] - a[j3 + 3];
+ a[j0 + 2] = x0r + x2r;
+ a[j0 + 3] = x0i - x2i;
+ a[j1 + 2] = x0r - x2r;
+ a[j1 + 3] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[j2 + 2] = wk1i * x0r - wk1r * x0i;
+ a[j2 + 3] = wk1i * x0i + wk1r * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[j3 + 2] = wk3i * x0r + wk3r * x0i;
+ a[j3 + 3] = wk3i * x0i - wk3r * x0r;
+ }
+
+ void cftmdl1(int n, float *a)
+ {
+ int i, i0, j, j0, j1, j2, j3, m, mh;
+ float ew, w1r, w1i, wk1r, wk1i, wk3r, wk3i,
+ wd1r, wd1i, wd3r, wd3i, ss1, ss3;
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+ mh = n >> 3;
+ m = 2 * mh;
+ j1 = m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[0] + a[j2];
+ x0i = a[1] + a[j2 + 1];
+ x1r = a[0] - a[j2];
+ x1i = a[1] - a[j2 + 1];
+ x2r = a[j1] + a[j3];
+ x2i = a[j1 + 1] + a[j3 + 1];
+ x3r = a[j1] - a[j3];
+ x3i = a[j1 + 1] - a[j3 + 1];
+ a[0] = x0r + x2r;
+ a[1] = x0i + x2i;
+ a[j1] = x0r - x2r;
+ a[j1 + 1] = x0i - x2i;
+ a[j2] = x1r - x3i;
+ a[j2 + 1] = x1i + x3r;
+ a[j3] = x1r + x3i;
+ a[j3 + 1] = x1i - x3r;
+ wd1r = 1;
+ wd1i = 0;
+ wd3r = 1;
+ wd3i = 0;
+ ew = M_PI_2 / m;
+ w1r = (float)cos(2 * ew);
+ w1i = (float)sin(2 * ew);
+ wk1r = w1r;
+ wk1i = w1i;
+ ss1 = 2 * w1i;
+ wk3i = 2 * ss1 * wk1r;
+ wk3r = wk1r - wk3i * wk1i;
+ wk3i = wk1i - wk3i * wk1r;
+ ss3 = 2 * wk3i;
+ i = 0;
+ for (;;) {
+ i0 = i + 4 * CDFT_LOOP_DIV;
+ if (i0 > mh - 4) {
+ i0 = mh - 4;
+ }
+ for (j = i + 2; j < i0; j += 4) {
+ wd1r -= ss1 * wk1i;
+ wd1i += ss1 * wk1r;
+ wd3r -= ss3 * wk3i;
+ wd3i += ss3 * wk3r;
+ j1 = j + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j] + a[j2];
+ x0i = a[j + 1] + a[j2 + 1];
+ x1r = a[j] - a[j2];
+ x1i = a[j + 1] - a[j2 + 1];
+ x2r = a[j1] + a[j3];
+ x2i = a[j1 + 1] + a[j3 + 1];
+ x3r = a[j1] - a[j3];
+ x3i = a[j1 + 1] - a[j3 + 1];
+ a[j] = x0r + x2r;
+ a[j + 1] = x0i + x2i;
+ a[j1] = x0r - x2r;
+ a[j1 + 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[j2] = wk1r * x0r - wk1i * x0i;
+ a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[j3] = wk3r * x0r + wk3i * x0i;
+ a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+ x0r = a[j + 2] + a[j2 + 2];
+ x0i = a[j + 3] + a[j2 + 3];
+ x1r = a[j + 2] - a[j2 + 2];
+ x1i = a[j + 3] - a[j2 + 3];
+ x2r = a[j1 + 2] + a[j3 + 2];
+ x2i = a[j1 + 3] + a[j3 + 3];
+ x3r = a[j1 + 2] - a[j3 + 2];
+ x3i = a[j1 + 3] - a[j3 + 3];
+ a[j + 2] = x0r + x2r;
+ a[j + 3] = x0i + x2i;
+ a[j1 + 2] = x0r - x2r;
+ a[j1 + 3] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[j2 + 2] = wd1r * x0r - wd1i * x0i;
+ a[j2 + 3] = wd1r * x0i + wd1i * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[j3 + 2] = wd3r * x0r + wd3i * x0i;
+ a[j3 + 3] = wd3r * x0i - wd3i * x0r;
+ j0 = m - j;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j0] + a[j2];
+ x0i = a[j0 + 1] + a[j2 + 1];
+ x1r = a[j0] - a[j2];
+ x1i = a[j0 + 1] - a[j2 + 1];
+ x2r = a[j1] + a[j3];
+ x2i = a[j1 + 1] + a[j3 + 1];
+ x3r = a[j1] - a[j3];
+ x3i = a[j1 + 1] - a[j3 + 1];
+ a[j0] = x0r + x2r;
+ a[j0 + 1] = x0i + x2i;
+ a[j1] = x0r - x2r;
+ a[j1 + 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[j2] = wk1i * x0r - wk1r * x0i;
+ a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[j3] = wk3i * x0r + wk3r * x0i;
+ a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+ x0r = a[j0 - 2] + a[j2 - 2];
+ x0i = a[j0 - 1] + a[j2 - 1];
+ x1r = a[j0 - 2] - a[j2 - 2];
+ x1i = a[j0 - 1] - a[j2 - 1];
+ x2r = a[j1 - 2] + a[j3 - 2];
+ x2i = a[j1 - 1] + a[j3 - 1];
+ x3r = a[j1 - 2] - a[j3 - 2];
+ x3i = a[j1 - 1] - a[j3 - 1];
+ a[j0 - 2] = x0r + x2r;
+ a[j0 - 1] = x0i + x2i;
+ a[j1 - 2] = x0r - x2r;
+ a[j1 - 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[j2 - 2] = wd1i * x0r - wd1r * x0i;
+ a[j2 - 1] = wd1i * x0i + wd1r * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[j3 - 2] = wd3i * x0r + wd3r * x0i;
+ a[j3 - 1] = wd3i * x0i - wd3r * x0r;
+ wk1r -= ss1 * wd1i;
+ wk1i += ss1 * wd1r;
+ wk3r -= ss3 * wd3i;
+ wk3i += ss3 * wd3r;
+ }
+ if (i0 == mh - 4) {
+ break;
+ }
+ wd1r = (float)cos(ew * i0);
+ wd1i = (float)sin(ew * i0);
+ wd3i = 4 * wd1i * wd1r;
+ wd3r = wd1r - wd3i * wd1i;
+ wd3i = wd1i - wd3i * wd1r;
+ wk1r = w1r * wd1r - w1i * wd1i;
+ wk1i = w1r * wd1i + w1i * wd1r;
+ wk3i = 4 * wk1i * wk1r;
+ wk3r = wk1r - wk3i * wk1i;
+ wk3i = wk1i - wk3i * wk1r;
+ i = i0;
+ }
+ wd1r = WR5000;
+ j0 = mh;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j0 - 2] + a[j2 - 2];
+ x0i = a[j0 - 1] + a[j2 - 1];
+ x1r = a[j0 - 2] - a[j2 - 2];
+ x1i = a[j0 - 1] - a[j2 - 1];
+ x2r = a[j1 - 2] + a[j3 - 2];
+ x2i = a[j1 - 1] + a[j3 - 1];
+ x3r = a[j1 - 2] - a[j3 - 2];
+ x3i = a[j1 - 1] - a[j3 - 1];
+ a[j0 - 2] = x0r + x2r;
+ a[j0 - 1] = x0i + x2i;
+ a[j1 - 2] = x0r - x2r;
+ a[j1 - 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[j2 - 2] = wk1r * x0r - wk1i * x0i;
+ a[j2 - 1] = wk1r * x0i + wk1i * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[j3 - 2] = wk3r * x0r + wk3i * x0i;
+ a[j3 - 1] = wk3r * x0i - wk3i * x0r;
+ x0r = a[j0] + a[j2];
+ x0i = a[j0 + 1] + a[j2 + 1];
+ x1r = a[j0] - a[j2];
+ x1i = a[j0 + 1] - a[j2 + 1];
+ x2r = a[j1] + a[j3];
+ x2i = a[j1 + 1] + a[j3 + 1];
+ x3r = a[j1] - a[j3];
+ x3i = a[j1 + 1] - a[j3 + 1];
+ a[j0] = x0r + x2r;
+ a[j0 + 1] = x0i + x2i;
+ a[j1] = x0r - x2r;
+ a[j1 + 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[j2] = wd1r * (x0r - x0i);
+ a[j2 + 1] = wd1r * (x0i + x0r);
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[j3] = -wd1r * (x0r + x0i);
+ a[j3 + 1] = -wd1r * (x0i - x0r);
+ x0r = a[j0 + 2] + a[j2 + 2];
+ x0i = a[j0 + 3] + a[j2 + 3];
+ x1r = a[j0 + 2] - a[j2 + 2];
+ x1i = a[j0 + 3] - a[j2 + 3];
+ x2r = a[j1 + 2] + a[j3 + 2];
+ x2i = a[j1 + 3] + a[j3 + 3];
+ x3r = a[j1 + 2] - a[j3 + 2];
+ x3i = a[j1 + 3] - a[j3 + 3];
+ a[j0 + 2] = x0r + x2r;
+ a[j0 + 3] = x0i + x2i;
+ a[j1 + 2] = x0r - x2r;
+ a[j1 + 3] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[j2 + 2] = wk1i * x0r - wk1r * x0i;
+ a[j2 + 3] = wk1i * x0i + wk1r * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[j3 + 2] = wk3i * x0r + wk3r * x0i;
+ a[j3 + 3] = wk3i * x0i - wk3r * x0r;
+ }
+
+
+ void cftmdl2(int n, float *a)
+ {
+ int i, i0, j, j0, j1, j2, j3, m, mh;
+ float ew, w1r, w1i, wn4r, wk1r, wk1i, wk3r, wk3i,
+ wl1r, wl1i, wl3r, wl3i, wd1r, wd1i, wd3r, wd3i,
+ we1r, we1i, we3r, we3i, ss1, ss3;
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i;
+
+ mh = n >> 3;
+ m = 2 * mh;
+ wn4r = WR5000;
+ j1 = m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[0] - a[j2 + 1];
+ x0i = a[1] + a[j2];
+ x1r = a[0] + a[j2 + 1];
+ x1i = a[1] - a[j2];
+ x2r = a[j1] - a[j3 + 1];
+ x2i = a[j1 + 1] + a[j3];
+ x3r = a[j1] + a[j3 + 1];
+ x3i = a[j1 + 1] - a[j3];
+ y0r = wn4r * (x2r - x2i);
+ y0i = wn4r * (x2i + x2r);
+ a[0] = x0r + y0r;
+ a[1] = x0i + y0i;
+ a[j1] = x0r - y0r;
+ a[j1 + 1] = x0i - y0i;
+ y0r = wn4r * (x3r - x3i);
+ y0i = wn4r * (x3i + x3r);
+ a[j2] = x1r - y0i;
+ a[j2 + 1] = x1i + y0r;
+ a[j3] = x1r + y0i;
+ a[j3 + 1] = x1i - y0r;
+ wl1r = 1;
+ wl1i = 0;
+ wl3r = 1;
+ wl3i = 0;
+ we1r = wn4r;
+ we1i = wn4r;
+ we3r = -wn4r;
+ we3i = -wn4r;
+ ew = M_PI_2 / (2 * m);
+ w1r = (float)cos(2 * ew);
+ w1i = (float)sin(2 * ew);
+ wk1r = w1r;
+ wk1i = w1i;
+ wd1r = wn4r * (w1r - w1i);
+ wd1i = wn4r * (w1i + w1r);
+ ss1 = 2 * w1i;
+ wk3i = 2 * ss1 * wk1r;
+ wk3r = wk1r - wk3i * wk1i;
+ wk3i = wk1i - wk3i * wk1r;
+ ss3 = 2 * wk3i;
+ wd3r = -wn4r * (wk3r - wk3i);
+ wd3i = -wn4r * (wk3i + wk3r);
+ i = 0;
+ for (;;) {
+ i0 = i + 4 * CDFT_LOOP_DIV;
+ if (i0 > mh - 4) {
+ i0 = mh - 4;
+ }
+ for (j = i + 2; j < i0; j += 4) {
+ wl1r -= ss1 * wk1i;
+ wl1i += ss1 * wk1r;
+ wl3r -= ss3 * wk3i;
+ wl3i += ss3 * wk3r;
+ we1r -= ss1 * wd1i;
+ we1i += ss1 * wd1r;
+ we3r -= ss3 * wd3i;
+ we3i += ss3 * wd3r;
+ j1 = j + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j] - a[j2 + 1];
+ x0i = a[j + 1] + a[j2];
+ x1r = a[j] + a[j2 + 1];
+ x1i = a[j + 1] - a[j2];
+ x2r = a[j1] - a[j3 + 1];
+ x2i = a[j1 + 1] + a[j3];
+ x3r = a[j1] + a[j3 + 1];
+ x3i = a[j1 + 1] - a[j3];
+ y0r = wk1r * x0r - wk1i * x0i;
+ y0i = wk1r * x0i + wk1i * x0r;
+ y2r = wd1r * x2r - wd1i * x2i;
+ y2i = wd1r * x2i + wd1i * x2r;
+ a[j] = y0r + y2r;
+ a[j + 1] = y0i + y2i;
+ a[j1] = y0r - y2r;
+ a[j1 + 1] = y0i - y2i;
+ y0r = wk3r * x1r + wk3i * x1i;
+ y0i = wk3r * x1i - wk3i * x1r;
+ y2r = wd3r * x3r + wd3i * x3i;
+ y2i = wd3r * x3i - wd3i * x3r;
+ a[j2] = y0r + y2r;
+ a[j2 + 1] = y0i + y2i;
+ a[j3] = y0r - y2r;
+ a[j3 + 1] = y0i - y2i;
+ x0r = a[j + 2] - a[j2 + 3];
+ x0i = a[j + 3] + a[j2 + 2];
+ x1r = a[j + 2] + a[j2 + 3];
+ x1i = a[j + 3] - a[j2 + 2];
+ x2r = a[j1 + 2] - a[j3 + 3];
+ x2i = a[j1 + 3] + a[j3 + 2];
+ x3r = a[j1 + 2] + a[j3 + 3];
+ x3i = a[j1 + 3] - a[j3 + 2];
+ y0r = wl1r * x0r - wl1i * x0i;
+ y0i = wl1r * x0i + wl1i * x0r;
+ y2r = we1r * x2r - we1i * x2i;
+ y2i = we1r * x2i + we1i * x2r;
+ a[j + 2] = y0r + y2r;
+ a[j + 3] = y0i + y2i;
+ a[j1 + 2] = y0r - y2r;
+ a[j1 + 3] = y0i - y2i;
+ y0r = wl3r * x1r + wl3i * x1i;
+ y0i = wl3r * x1i - wl3i * x1r;
+ y2r = we3r * x3r + we3i * x3i;
+ y2i = we3r * x3i - we3i * x3r;
+ a[j2 + 2] = y0r + y2r;
+ a[j2 + 3] = y0i + y2i;
+ a[j3 + 2] = y0r - y2r;
+ a[j3 + 3] = y0i - y2i;
+ j0 = m - j;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j0] - a[j2 + 1];
+ x0i = a[j0 + 1] + a[j2];
+ x1r = a[j0] + a[j2 + 1];
+ x1i = a[j0 + 1] - a[j2];
+ x2r = a[j1] - a[j3 + 1];
+ x2i = a[j1 + 1] + a[j3];
+ x3r = a[j1] + a[j3 + 1];
+ x3i = a[j1 + 1] - a[j3];
+ y0r = wd1i * x0r - wd1r * x0i;
+ y0i = wd1i * x0i + wd1r * x0r;
+ y2r = wk1i * x2r - wk1r * x2i;
+ y2i = wk1i * x2i + wk1r * x2r;
+ a[j0] = y0r + y2r;
+ a[j0 + 1] = y0i + y2i;
+ a[j1] = y0r - y2r;
+ a[j1 + 1] = y0i - y2i;
+ y0r = wd3i * x1r + wd3r * x1i;
+ y0i = wd3i * x1i - wd3r * x1r;
+ y2r = wk3i * x3r + wk3r * x3i;
+ y2i = wk3i * x3i - wk3r * x3r;
+ a[j2] = y0r + y2r;
+ a[j2 + 1] = y0i + y2i;
+ a[j3] = y0r - y2r;
+ a[j3 + 1] = y0i - y2i;
+ x0r = a[j0 - 2] - a[j2 - 1];
+ x0i = a[j0 - 1] + a[j2 - 2];
+ x1r = a[j0 - 2] + a[j2 - 1];
+ x1i = a[j0 - 1] - a[j2 - 2];
+ x2r = a[j1 - 2] - a[j3 - 1];
+ x2i = a[j1 - 1] + a[j3 - 2];
+ x3r = a[j1 - 2] + a[j3 - 1];
+ x3i = a[j1 - 1] - a[j3 - 2];
+ y0r = we1i * x0r - we1r * x0i;
+ y0i = we1i * x0i + we1r * x0r;
+ y2r = wl1i * x2r - wl1r * x2i;
+ y2i = wl1i * x2i + wl1r * x2r;
+ a[j0 - 2] = y0r + y2r;
+ a[j0 - 1] = y0i + y2i;
+ a[j1 - 2] = y0r - y2r;
+ a[j1 - 1] = y0i - y2i;
+ y0r = we3i * x1r + we3r * x1i;
+ y0i = we3i * x1i - we3r * x1r;
+ y2r = wl3i * x3r + wl3r * x3i;
+ y2i = wl3i * x3i - wl3r * x3r;
+ a[j2 - 2] = y0r + y2r;
+ a[j2 - 1] = y0i + y2i;
+ a[j3 - 2] = y0r - y2r;
+ a[j3 - 1] = y0i - y2i;
+ wk1r -= ss1 * wl1i;
+ wk1i += ss1 * wl1r;
+ wk3r -= ss3 * wl3i;
+ wk3i += ss3 * wl3r;
+ wd1r -= ss1 * we1i;
+ wd1i += ss1 * we1r;
+ wd3r -= ss3 * we3i;
+ wd3i += ss3 * we3r;
+ }
+ if (i0 == mh - 4) {
+ break;
+ }
+ wl1r = (float)cos(ew * i0);
+ wl1i = (float)sin(ew * i0);
+ wl3i = 4 * wl1i * wl1r;
+ wl3r = wl1r - wl3i * wl1i;
+ wl3i = wl1i - wl3i * wl1r;
+ we1r = wn4r * (wl1r - wl1i);
+ we1i = wn4r * (wl1i + wl1r);
+ we3r = -wn4r * (wl3r - wl3i);
+ we3i = -wn4r * (wl3i + wl3r);
+ wk1r = w1r * wl1r - w1i * wl1i;
+ wk1i = w1r * wl1i + w1i * wl1r;
+ wk3i = 4 * wk1i * wk1r;
+ wk3r = wk1r - wk3i * wk1i;
+ wk3i = wk1i - wk3i * wk1r;
+ wd1r = wn4r * (wk1r - wk1i);
+ wd1i = wn4r * (wk1i + wk1r);
+ wd3r = -wn4r * (wk3r - wk3i);
+ wd3i = -wn4r * (wk3i + wk3r);
+ i = i0;
+ }
+ wl1r = WR2500;
+ wl1i = WI2500;
+ j0 = mh;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ x0r = a[j0 - 2] - a[j2 - 1];
+ x0i = a[j0 - 1] + a[j2 - 2];
+ x1r = a[j0 - 2] + a[j2 - 1];
+ x1i = a[j0 - 1] - a[j2 - 2];
+ x2r = a[j1 - 2] - a[j3 - 1];
+ x2i = a[j1 - 1] + a[j3 - 2];
+ x3r = a[j1 - 2] + a[j3 - 1];
+ x3i = a[j1 - 1] - a[j3 - 2];
+ y0r = wk1r * x0r - wk1i * x0i;
+ y0i = wk1r * x0i + wk1i * x0r;
+ y2r = wd1r * x2r - wd1i * x2i;
+ y2i = wd1r * x2i + wd1i * x2r;
+ a[j0 - 2] = y0r + y2r;
+ a[j0 - 1] = y0i + y2i;
+ a[j1 - 2] = y0r - y2r;
+ a[j1 - 1] = y0i - y2i;
+ y0r = wk3r * x1r + wk3i * x1i;
+ y0i = wk3r * x1i - wk3i * x1r;
+ y2r = wd3r * x3r + wd3i * x3i;
+ y2i = wd3r * x3i - wd3i * x3r;
+ a[j2 - 2] = y0r + y2r;
+ a[j2 - 1] = y0i + y2i;
+ a[j3 - 2] = y0r - y2r;
+ a[j3 - 1] = y0i - y2i;
+ x0r = a[j0] - a[j2 + 1];
+ x0i = a[j0 + 1] + a[j2];
+ x1r = a[j0] + a[j2 + 1];
+ x1i = a[j0 + 1] - a[j2];
+ x2r = a[j1] - a[j3 + 1];
+ x2i = a[j1 + 1] + a[j3];
+ x3r = a[j1] + a[j3 + 1];
+ x3i = a[j1 + 1] - a[j3];
+ y0r = wl1r * x0r - wl1i * x0i;
+ y0i = wl1r * x0i + wl1i * x0r;
+ y2r = wl1i * x2r - wl1r * x2i;
+ y2i = wl1i * x2i + wl1r * x2r;
+ a[j0] = y0r + y2r;
+ a[j0 + 1] = y0i + y2i;
+ a[j1] = y0r - y2r;
+ a[j1 + 1] = y0i - y2i;
+ y0r = wl1i * x1r - wl1r * x1i;
+ y0i = wl1i * x1i + wl1r * x1r;
+ y2r = wl1r * x3r - wl1i * x3i;
+ y2i = wl1r * x3i + wl1i * x3r;
+ a[j2] = y0r - y2r;
+ a[j2 + 1] = y0i - y2i;
+ a[j3] = y0r + y2r;
+ a[j3 + 1] = y0i + y2i;
+ x0r = a[j0 + 2] - a[j2 + 3];
+ x0i = a[j0 + 3] + a[j2 + 2];
+ x1r = a[j0 + 2] + a[j2 + 3];
+ x1i = a[j0 + 3] - a[j2 + 2];
+ x2r = a[j1 + 2] - a[j3 + 3];
+ x2i = a[j1 + 3] + a[j3 + 2];
+ x3r = a[j1 + 2] + a[j3 + 3];
+ x3i = a[j1 + 3] - a[j3 + 2];
+ y0r = wd1i * x0r - wd1r * x0i;
+ y0i = wd1i * x0i + wd1r * x0r;
+ y2r = wk1i * x2r - wk1r * x2i;
+ y2i = wk1i * x2i + wk1r * x2r;
+ a[j0 + 2] = y0r + y2r;
+ a[j0 + 3] = y0i + y2i;
+ a[j1 + 2] = y0r - y2r;
+ a[j1 + 3] = y0i - y2i;
+ y0r = wd3i * x1r + wd3r * x1i;
+ y0i = wd3i * x1i - wd3r * x1r;
+ y2r = wk3i * x3r + wk3r * x3i;
+ y2i = wk3i * x3i - wk3r * x3r;
+ a[j2 + 2] = y0r + y2r;
+ a[j2 + 3] = y0i + y2i;
+ a[j3 + 2] = y0r - y2r;
+ a[j3 + 3] = y0i - y2i;
+ }
+
+ void cftf161(float *a)
+ {
+ float wn4r, wk1r, wk1i,
+ x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,
+ y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,
+ y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i,
+ y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i,
+ y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+
+ wn4r = WR5000;
+ wk1r = WR2500;
+ wk1i = WI2500;
+ x0r = a[0] + a[16];
+ x0i = a[1] + a[17];
+ x1r = a[0] - a[16];
+ x1i = a[1] - a[17];
+ x2r = a[8] + a[24];
+ x2i = a[9] + a[25];
+ x3r = a[8] - a[24];
+ x3i = a[9] - a[25];
+ y0r = x0r + x2r;
+ y0i = x0i + x2i;
+ y4r = x0r - x2r;
+ y4i = x0i - x2i;
+ y8r = x1r - x3i;
+ y8i = x1i + x3r;
+ y12r = x1r + x3i;
+ y12i = x1i - x3r;
+ x0r = a[2] + a[18];
+ x0i = a[3] + a[19];
+ x1r = a[2] - a[18];
+ x1i = a[3] - a[19];
+ x2r = a[10] + a[26];
+ x2i = a[11] + a[27];
+ x3r = a[10] - a[26];
+ x3i = a[11] - a[27];
+ y1r = x0r + x2r;
+ y1i = x0i + x2i;
+ y5r = x0r - x2r;
+ y5i = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ y9r = wk1r * x0r - wk1i * x0i;
+ y9i = wk1r * x0i + wk1i * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ y13r = wk1i * x0r - wk1r * x0i;
+ y13i = wk1i * x0i + wk1r * x0r;
+ x0r = a[4] + a[20];
+ x0i = a[5] + a[21];
+ x1r = a[4] - a[20];
+ x1i = a[5] - a[21];
+ x2r = a[12] + a[28];
+ x2i = a[13] + a[29];
+ x3r = a[12] - a[28];
+ x3i = a[13] - a[29];
+ y2r = x0r + x2r;
+ y2i = x0i + x2i;
+ y6r = x0r - x2r;
+ y6i = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ y10r = wn4r * (x0r - x0i);
+ y10i = wn4r * (x0i + x0r);
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ y14r = wn4r * (x0r + x0i);
+ y14i = wn4r * (x0i - x0r);
+ x0r = a[6] + a[22];
+ x0i = a[7] + a[23];
+ x1r = a[6] - a[22];
+ x1i = a[7] - a[23];
+ x2r = a[14] + a[30];
+ x2i = a[15] + a[31];
+ x3r = a[14] - a[30];
+ x3i = a[15] - a[31];
+ y3r = x0r + x2r;
+ y3i = x0i + x2i;
+ y7r = x0r - x2r;
+ y7i = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ y11r = wk1i * x0r - wk1r * x0i;
+ y11i = wk1i * x0i + wk1r * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ y15r = wk1r * x0r - wk1i * x0i;
+ y15i = wk1r * x0i + wk1i * x0r;
+ x0r = y12r - y14r;
+ x0i = y12i - y14i;
+ x1r = y12r + y14r;
+ x1i = y12i + y14i;
+ x2r = y13r - y15r;
+ x2i = y13i - y15i;
+ x3r = y13r + y15r;
+ x3i = y13i + y15i;
+ a[24] = x0r + x2r;
+ a[25] = x0i + x2i;
+ a[26] = x0r - x2r;
+ a[27] = x0i - x2i;
+ a[28] = x1r - x3i;
+ a[29] = x1i + x3r;
+ a[30] = x1r + x3i;
+ a[31] = x1i - x3r;
+ x0r = y8r + y10r;
+ x0i = y8i + y10i;
+ x1r = y8r - y10r;
+ x1i = y8i - y10i;
+ x2r = y9r + y11r;
+ x2i = y9i + y11i;
+ x3r = y9r - y11r;
+ x3i = y9i - y11i;
+ a[16] = x0r + x2r;
+ a[17] = x0i + x2i;
+ a[18] = x0r - x2r;
+ a[19] = x0i - x2i;
+ a[20] = x1r - x3i;
+ a[21] = x1i + x3r;
+ a[22] = x1r + x3i;
+ a[23] = x1i - x3r;
+ x0r = y5r - y7i;
+ x0i = y5i + y7r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ x0r = y5r + y7i;
+ x0i = y5i - y7r;
+ x3r = wn4r * (x0r - x0i);
+ x3i = wn4r * (x0i + x0r);
+ x0r = y4r - y6i;
+ x0i = y4i + y6r;
+ x1r = y4r + y6i;
+ x1i = y4i - y6r;
+ a[8] = x0r + x2r;
+ a[9] = x0i + x2i;
+ a[10] = x0r - x2r;
+ a[11] = x0i - x2i;
+ a[12] = x1r - x3i;
+ a[13] = x1i + x3r;
+ a[14] = x1r + x3i;
+ a[15] = x1i - x3r;
+ x0r = y0r + y2r;
+ x0i = y0i + y2i;
+ x1r = y0r - y2r;
+ x1i = y0i - y2i;
+ x2r = y1r + y3r;
+ x2i = y1i + y3i;
+ x3r = y1r - y3r;
+ x3i = y1i - y3i;
+ a[0] = x0r + x2r;
+ a[1] = x0i + x2i;
+ a[2] = x0r - x2r;
+ a[3] = x0i - x2i;
+ a[4] = x1r - x3i;
+ a[5] = x1i + x3r;
+ a[6] = x1r + x3i;
+ a[7] = x1i - x3r;
+ }
+
+
+ void cftf162(float *a)
+ {
+ float wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i,
+ x0r, x0i, x1r, x1i, x2r, x2i,
+ y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,
+ y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i,
+ y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i,
+ y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+
+ wn4r = WR5000;
+ wk1r = WR1250;
+ wk1i = WI1250;
+ wk2r = WR2500;
+ wk2i = WI2500;
+ wk3r = WR3750;
+ wk3i = WI3750;
+ x1r = a[0] - a[17];
+ x1i = a[1] + a[16];
+ x0r = a[8] - a[25];
+ x0i = a[9] + a[24];
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ y0r = x1r + x2r;
+ y0i = x1i + x2i;
+ y4r = x1r - x2r;
+ y4i = x1i - x2i;
+ x1r = a[0] + a[17];
+ x1i = a[1] - a[16];
+ x0r = a[8] + a[25];
+ x0i = a[9] - a[24];
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ y8r = x1r - x2i;
+ y8i = x1i + x2r;
+ y12r = x1r + x2i;
+ y12i = x1i - x2r;
+ x0r = a[2] - a[19];
+ x0i = a[3] + a[18];
+ x1r = wk1r * x0r - wk1i * x0i;
+ x1i = wk1r * x0i + wk1i * x0r;
+ x0r = a[10] - a[27];
+ x0i = a[11] + a[26];
+ x2r = wk3i * x0r - wk3r * x0i;
+ x2i = wk3i * x0i + wk3r * x0r;
+ y1r = x1r + x2r;
+ y1i = x1i + x2i;
+ y5r = x1r - x2r;
+ y5i = x1i - x2i;
+ x0r = a[2] + a[19];
+ x0i = a[3] - a[18];
+ x1r = wk3r * x0r - wk3i * x0i;
+ x1i = wk3r * x0i + wk3i * x0r;
+ x0r = a[10] + a[27];
+ x0i = a[11] - a[26];
+ x2r = wk1r * x0r + wk1i * x0i;
+ x2i = wk1r * x0i - wk1i * x0r;
+ y9r = x1r - x2r;
+ y9i = x1i - x2i;
+ y13r = x1r + x2r;
+ y13i = x1i + x2i;
+ x0r = a[4] - a[21];
+ x0i = a[5] + a[20];
+ x1r = wk2r * x0r - wk2i * x0i;
+ x1i = wk2r * x0i + wk2i * x0r;
+ x0r = a[12] - a[29];
+ x0i = a[13] + a[28];
+ x2r = wk2i * x0r - wk2r * x0i;
+ x2i = wk2i * x0i + wk2r * x0r;
+ y2r = x1r + x2r;
+ y2i = x1i + x2i;
+ y6r = x1r - x2r;
+ y6i = x1i - x2i;
+ x0r = a[4] + a[21];
+ x0i = a[5] - a[20];
+ x1r = wk2i * x0r - wk2r * x0i;
+ x1i = wk2i * x0i + wk2r * x0r;
+ x0r = a[12] + a[29];
+ x0i = a[13] - a[28];
+ x2r = wk2r * x0r - wk2i * x0i;
+ x2i = wk2r * x0i + wk2i * x0r;
+ y10r = x1r - x2r;
+ y10i = x1i - x2i;
+ y14r = x1r + x2r;
+ y14i = x1i + x2i;
+ x0r = a[6] - a[23];
+ x0i = a[7] + a[22];
+ x1r = wk3r * x0r - wk3i * x0i;
+ x1i = wk3r * x0i + wk3i * x0r;
+ x0r = a[14] - a[31];
+ x0i = a[15] + a[30];
+ x2r = wk1i * x0r - wk1r * x0i;
+ x2i = wk1i * x0i + wk1r * x0r;
+ y3r = x1r + x2r;
+ y3i = x1i + x2i;
+ y7r = x1r - x2r;
+ y7i = x1i - x2i;
+ x0r = a[6] + a[23];
+ x0i = a[7] - a[22];
+ x1r = wk1i * x0r + wk1r * x0i;
+ x1i = wk1i * x0i - wk1r * x0r;
+ x0r = a[14] + a[31];
+ x0i = a[15] - a[30];
+ x2r = wk3i * x0r - wk3r * x0i;
+ x2i = wk3i * x0i + wk3r * x0r;
+ y11r = x1r + x2r;
+ y11i = x1i + x2i;
+ y15r = x1r - x2r;
+ y15i = x1i - x2i;
+ x1r = y0r + y2r;
+ x1i = y0i + y2i;
+ x2r = y1r + y3r;
+ x2i = y1i + y3i;
+ a[0] = x1r + x2r;
+ a[1] = x1i + x2i;
+ a[2] = x1r - x2r;
+ a[3] = x1i - x2i;
+ x1r = y0r - y2r;
+ x1i = y0i - y2i;
+ x2r = y1r - y3r;
+ x2i = y1i - y3i;
+ a[4] = x1r - x2i;
+ a[5] = x1i + x2r;
+ a[6] = x1r + x2i;
+ a[7] = x1i - x2r;
+ x1r = y4r - y6i;
+ x1i = y4i + y6r;
+ x0r = y5r - y7i;
+ x0i = y5i + y7r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ a[8] = x1r + x2r;
+ a[9] = x1i + x2i;
+ a[10] = x1r - x2r;
+ a[11] = x1i - x2i;
+ x1r = y4r + y6i;
+ x1i = y4i - y6r;
+ x0r = y5r + y7i;
+ x0i = y5i - y7r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ a[12] = x1r - x2i;
+ a[13] = x1i + x2r;
+ a[14] = x1r + x2i;
+ a[15] = x1i - x2r;
+ x1r = y8r + y10r;
+ x1i = y8i + y10i;
+ x2r = y9r - y11r;
+ x2i = y9i - y11i;
+ a[16] = x1r + x2r;
+ a[17] = x1i + x2i;
+ a[18] = x1r - x2r;
+ a[19] = x1i - x2i;
+ x1r = y8r - y10r;
+ x1i = y8i - y10i;
+ x2r = y9r + y11r;
+ x2i = y9i + y11i;
+ a[20] = x1r - x2i;
+ a[21] = x1i + x2r;
+ a[22] = x1r + x2i;
+ a[23] = x1i - x2r;
+ x1r = y12r - y14i;
+ x1i = y12i + y14r;
+ x0r = y13r + y15i;
+ x0i = y13i - y15r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ a[24] = x1r + x2r;
+ a[25] = x1i + x2i;
+ a[26] = x1r - x2r;
+ a[27] = x1i - x2i;
+ x1r = y12r + y14i;
+ x1i = y12i - y14r;
+ x0r = y13r - y15i;
+ x0i = y13i + y15r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ a[28] = x1r - x2i;
+ a[29] = x1i + x2r;
+ a[30] = x1r + x2i;
+ a[31] = x1i - x2r;
+ }
+
+
+ void cftf081(float *a)
+ {
+ float wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i,
+ y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,
+ y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+
+ wn4r = WR5000;
+ x0r = a[0] + a[8];
+ x0i = a[1] + a[9];
+ x1r = a[0] - a[8];
+ x1i = a[1] - a[9];
+ x2r = a[4] + a[12];
+ x2i = a[5] + a[13];
+ x3r = a[4] - a[12];
+ x3i = a[5] - a[13];
+ y0r = x0r + x2r;
+ y0i = x0i + x2i;
+ y2r = x0r - x2r;
+ y2i = x0i - x2i;
+ y1r = x1r - x3i;
+ y1i = x1i + x3r;
+ y3r = x1r + x3i;
+ y3i = x1i - x3r;
+ x0r = a[2] + a[10];
+ x0i = a[3] + a[11];
+ x1r = a[2] - a[10];
+ x1i = a[3] - a[11];
+ x2r = a[6] + a[14];
+ x2i = a[7] + a[15];
+ x3r = a[6] - a[14];
+ x3i = a[7] - a[15];
+ y4r = x0r + x2r;
+ y4i = x0i + x2i;
+ y6r = x0r - x2r;
+ y6i = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ x2r = x1r + x3i;
+ x2i = x1i - x3r;
+ y5r = wn4r * (x0r - x0i);
+ y5i = wn4r * (x0r + x0i);
+ y7r = wn4r * (x2r - x2i);
+ y7i = wn4r * (x2r + x2i);
+ a[8] = y1r + y5r;
+ a[9] = y1i + y5i;
+ a[10] = y1r - y5r;
+ a[11] = y1i - y5i;
+ a[12] = y3r - y7i;
+ a[13] = y3i + y7r;
+ a[14] = y3r + y7i;
+ a[15] = y3i - y7r;
+ a[0] = y0r + y4r;
+ a[1] = y0i + y4i;
+ a[2] = y0r - y4r;
+ a[3] = y0i - y4i;
+ a[4] = y2r - y6i;
+ a[5] = y2i + y6r;
+ a[6] = y2r + y6i;
+ a[7] = y2i - y6r;
+ }
+
+
+ void cftf082(float *a)
+ {
+ float wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i,
+ y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i,
+ y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+
+ wn4r = WR5000;
+ wk1r = WR2500;
+ wk1i = WI2500;
+ y0r = a[0] - a[9];
+ y0i = a[1] + a[8];
+ y1r = a[0] + a[9];
+ y1i = a[1] - a[8];
+ x0r = a[4] - a[13];
+ x0i = a[5] + a[12];
+ y2r = wn4r * (x0r - x0i);
+ y2i = wn4r * (x0i + x0r);
+ x0r = a[4] + a[13];
+ x0i = a[5] - a[12];
+ y3r = wn4r * (x0r - x0i);
+ y3i = wn4r * (x0i + x0r);
+ x0r = a[2] - a[11];
+ x0i = a[3] + a[10];
+ y4r = wk1r * x0r - wk1i * x0i;
+ y4i = wk1r * x0i + wk1i * x0r;
+ x0r = a[2] + a[11];
+ x0i = a[3] - a[10];
+ y5r = wk1i * x0r - wk1r * x0i;
+ y5i = wk1i * x0i + wk1r * x0r;
+ x0r = a[6] - a[15];
+ x0i = a[7] + a[14];
+ y6r = wk1i * x0r - wk1r * x0i;
+ y6i = wk1i * x0i + wk1r * x0r;
+ x0r = a[6] + a[15];
+ x0i = a[7] - a[14];
+ y7r = wk1r * x0r - wk1i * x0i;
+ y7i = wk1r * x0i + wk1i * x0r;
+ x0r = y0r + y2r;
+ x0i = y0i + y2i;
+ x1r = y4r + y6r;
+ x1i = y4i + y6i;
+ a[0] = x0r + x1r;
+ a[1] = x0i + x1i;
+ a[2] = x0r - x1r;
+ a[3] = x0i - x1i;
+ x0r = y0r - y2r;
+ x0i = y0i - y2i;
+ x1r = y4r - y6r;
+ x1i = y4i - y6i;
+ a[4] = x0r - x1i;
+ a[5] = x0i + x1r;
+ a[6] = x0r + x1i;
+ a[7] = x0i - x1r;
+ x0r = y1r - y3i;
+ x0i = y1i + y3r;
+ x1r = y5r - y7r;
+ x1i = y5i - y7i;
+ a[8] = x0r + x1r;
+ a[9] = x0i + x1i;
+ a[10] = x0r - x1r;
+ a[11] = x0i - x1i;
+ x0r = y1r + y3i;
+ x0i = y1i - y3r;
+ x1r = y5r + y7r;
+ x1i = y5i + y7i;
+ a[12] = x0r - x1i;
+ a[13] = x0i + x1r;
+ a[14] = x0r + x1i;
+ a[15] = x0i - x1r;
+ }
+
+ void cftf040(float *a)
+ {
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+ x0r = a[0] + a[4];
+ x0i = a[1] + a[5];
+ x1r = a[0] - a[4];
+ x1i = a[1] - a[5];
+ x2r = a[2] + a[6];
+ x2i = a[3] + a[7];
+ x3r = a[2] - a[6];
+ x3i = a[3] - a[7];
+ a[0] = x0r + x2r;
+ a[1] = x0i + x2i;
+ a[2] = x1r - x3i;
+ a[3] = x1i + x3r;
+ a[4] = x0r - x2r;
+ a[5] = x0i - x2i;
+ a[6] = x1r + x3i;
+ a[7] = x1i - x3r;
+ }
+
+ void cftb040(float *a)
+ {
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+ x0r = a[0] + a[4];
+ x0i = a[1] + a[5];
+ x1r = a[0] - a[4];
+ x1i = a[1] - a[5];
+ x2r = a[2] + a[6];
+ x2i = a[3] + a[7];
+ x3r = a[2] - a[6];
+ x3i = a[3] - a[7];
+ a[0] = x0r + x2r;
+ a[1] = x0i + x2i;
+ a[2] = x1r + x3i;
+ a[3] = x1i - x3r;
+ a[4] = x0r - x2r;
+ a[5] = x0i - x2i;
+ a[6] = x1r - x3i;
+ a[7] = x1i + x3r;
+ }
+
+ void cftx020(float *a)
+ {
+ float x0r, x0i;
+
+ x0r = a[0] - a[2];
+ x0i = a[1] - a[3];
+ a[0] += a[2];
+ a[1] += a[3];
+ a[2] = x0r;
+ a[3] = x0i;
+ }
+
+ void cftfx41(int n, float *a)
+ {
+ if (n == 128) {
+ cftf161(a);
+ cftf162(&a[32]);
+ cftf161(&a[64]);
+ cftf161(&a[96]);
+ }
+ else {
+ cftf081(a);
+ cftf082(&a[16]);
+ cftf081(&a[32]);
+ cftf081(&a[48]);
+ }
+ }
+
+ int cfttree(int n, int j, int k, float *a)
+ {
+ int i, isplt, m;
+
+ if ((k & 3) != 0) {
+ isplt = k & 1;
+ if (isplt != 0) {
+ cftmdl1(n, &a[j - n]);
+ }
+ else {
+ cftmdl2(n, &a[j - n]);
+ }
+ }
+ else {
+ m = n;
+ for (i = k; (i & 3) == 0; i >>= 2) {
+ m <<= 2;
+ }
+ isplt = i & 1;
+ if (isplt != 0) {
+ while (m > 128) {
+ cftmdl1(m, &a[j - m]);
+ m >>= 2;
+ }
+ }
+ else {
+ while (m > 128) {
+ cftmdl2(m, &a[j - m]);
+ m >>= 2;
+ }
+ }
+ }
+ return isplt;
+ }
+
+ void cftleaf(int n, int isplt, float *a)
+ {
+ if (n == 512) {
+ cftmdl1(128, a);
+ cftf161(a);
+ cftf162(&a[32]);
+ cftf161(&a[64]);
+ cftf161(&a[96]);
+ cftmdl2(128, &a[128]);
+ cftf161(&a[128]);
+ cftf162(&a[160]);
+ cftf161(&a[192]);
+ cftf162(&a[224]);
+ cftmdl1(128, &a[256]);
+ cftf161(&a[256]);
+ cftf162(&a[288]);
+ cftf161(&a[320]);
+ cftf161(&a[352]);
+ if (isplt != 0) {
+ cftmdl1(128, &a[384]);
+ cftf161(&a[480]);
+ }
+ else {
+ cftmdl2(128, &a[384]);
+ cftf162(&a[480]);
+ }
+ cftf161(&a[384]);
+ cftf162(&a[416]);
+ cftf161(&a[448]);
+ }
+ else {
+ cftmdl1(64, a);
+ cftf081(a);
+ cftf082(&a[16]);
+ cftf081(&a[32]);
+ cftf081(&a[48]);
+ cftmdl2(64, &a[64]);
+ cftf081(&a[64]);
+ cftf082(&a[80]);
+ cftf081(&a[96]);
+ cftf082(&a[112]);
+ cftmdl1(64, &a[128]);
+ cftf081(&a[128]);
+ cftf082(&a[144]);
+ cftf081(&a[160]);
+ cftf081(&a[176]);
+ if (isplt != 0) {
+ cftmdl1(64, &a[192]);
+ cftf081(&a[240]);
+ }
+ else {
+ cftmdl2(64, &a[192]);
+ cftf082(&a[240]);
+ }
+ cftf081(&a[192]);
+ cftf082(&a[208]);
+ cftf081(&a[224]);
+ }
+ }
+
+ void cftrec4(int n, float *a)
+ {
+ int isplt, j, k, m;
+
+ m = n;
+ while (m > 512) {
+ m >>= 2;
+ cftmdl1(m, &a[n - m]);
+ }
+ cftleaf(m, 1, &a[n - m]);
+ k = 0;
+ for (j = n - m; j > 0; j -= m) {
+ k++;
+ isplt = cfttree(m, j, k, a);
+ cftleaf(m, isplt, &a[j - m]);
+ }
+ }
+
+ void cftfsub(int n, float *a)
+ {
+ if (n > 8) {
+ if (n > 32) {
+ cftmdl1(n, a);
+ if (n > 512) {
+ cftrec4(n, a);
+ }
+ else if (n > 128) {
+ cftleaf(n, 1, a);
+ }
+ else {
+ cftfx41(n, a);
+ }
+ bitrv2(n, a);
+ }
+ else if (n == 32) {
+ cftf161(a);
+ bitrv216(a);
+ }
+ else {
+ cftf081(a);
+ bitrv208(a);
+ }
+ }
+ else if (n == 8) {
+ cftf040(a);
+ }
+ else if (n == 4) {
+ cftx020(a);
+ }
+ }
+
+
+ void cftbsub(int n, float *a)
+ {
+ if (n > 8) {
+ if (n > 32) {
+ cftb1st(n, a);
+ if (n > 512) {
+ cftrec4(n, a);
+ }
+ else if (n > 128) {
+ cftleaf(n, 1, a);
+ }
+ else {
+ cftfx41(n, a);
+ }
+ bitrv2conj(n, a);
+ }
+ else if (n == 32) {
+ cftf161(a);
+ bitrv216neg(a);
+ }
+ else {
+ cftf081(a);
+ bitrv208neg(a);
+ }
+ }
+ else if (n == 8) {
+ cftb040(a);
+ }
+ else if (n == 4) {
+ cftx020(a);
+ }
+ }
+} // fftimpl
+
+
+namespace SoLoud
+{
+ namespace FFT
+ {
+ void fft1024(float *aBuffer)
+ {
+ fft(aBuffer, 1024);
+ }
+
+ void fft256(float *aBuffer)
+ {
+ fft(aBuffer, 256);
+ }
+
+ void ifft256(float *aBuffer)
+ {
+ ifft(aBuffer, 256);
+ }
+
+ void fft(float *aBuffer, unsigned int aBufferLength)
+ {
+ fftimpl::cftbsub(aBufferLength, aBuffer);
+ }
+
+ void ifft(float *aBuffer, unsigned int aBufferLength)
+ {
+ unsigned int i;
+ fftimpl::cftfsub(aBufferLength, aBuffer);
+ for (i = 0; i < aBufferLength; i++)
+ aBuffer[i] *= 1.0f / (aBufferLength / 2);
+ }
+ };
+};
diff --git a/src/soloud/src/core/soloud_fft_lut.cpp b/src/soloud/src/core/soloud_fft_lut.cpp
new file mode 100644
index 0000000..cfbefa5
--- /dev/null
+++ b/src/soloud/src/core/soloud_fft_lut.cpp
@@ -0,0 +1,339 @@
+/* **************************************************
+ * WARNING: this is a generated file. Do not edit. *
+ * Any edits will be overwritten by the generator. *
+ ************************************************** */
+
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/* SoLoud Lookup Table Generator (c)2015 Jari Komppa http://iki.fi/sol/ */
+
+int Soloud_fft_bitrev_10[1024] = {
+0, 512, 256, 768, 128, 640, 384, 896, 64, 576, 320, 832, 192, 704, 448,
+960, 32, 544, 288, 800, 160, 672, 416, 928, 96, 608, 352, 864, 224, 736,
+480, 992, 16, 528, 272, 784, 144, 656, 400, 912, 80, 592, 336, 848, 208,
+720, 464, 976, 48, 560, 304, 816, 176, 688, 432, 944, 112, 624, 368, 880,
+240, 752, 496, 1008, 8, 520, 264, 776, 136, 648, 392, 904, 72, 584, 328,
+840, 200, 712, 456, 968, 40, 552, 296, 808, 168, 680, 424, 936, 104, 616,
+360, 872, 232, 744, 488, 1000, 24, 536, 280, 792, 152, 664, 408, 920, 88,
+600, 344, 856, 216, 728, 472, 984, 56, 568, 312, 824, 184, 696, 440, 952,
+120, 632, 376, 888, 248, 760, 504, 1016, 4, 516, 260, 772, 132, 644, 388,
+900, 68, 580, 324, 836, 196, 708, 452, 964, 36, 548, 292, 804, 164, 676,
+420, 932, 100, 612, 356, 868, 228, 740, 484, 996, 20, 532, 276, 788, 148,
+660, 404, 916, 84, 596, 340, 852, 212, 724, 468, 980, 52, 564, 308, 820,
+180, 692, 436, 948, 116, 628, 372, 884, 244, 756, 500, 1012, 12, 524, 268,
+780, 140, 652, 396, 908, 76, 588, 332, 844, 204, 716, 460, 972, 44, 556,
+300, 812, 172, 684, 428, 940, 108, 620, 364, 876, 236, 748, 492, 1004,
+28, 540, 284, 796, 156, 668, 412, 924, 92, 604, 348, 860, 220, 732, 476,
+988, 60, 572, 316, 828, 188, 700, 444, 956, 124, 636, 380, 892, 252, 764,
+508, 1020, 2, 514, 258, 770, 130, 642, 386, 898, 66, 578, 322, 834, 194,
+706, 450, 962, 34, 546, 290, 802, 162, 674, 418, 930, 98, 610, 354, 866,
+226, 738, 482, 994, 18, 530, 274, 786, 146, 658, 402, 914, 82, 594, 338,
+850, 210, 722, 466, 978, 50, 562, 306, 818, 178, 690, 434, 946, 114, 626,
+370, 882, 242, 754, 498, 1010, 10, 522, 266, 778, 138, 650, 394, 906, 74,
+586, 330, 842, 202, 714, 458, 970, 42, 554, 298, 810, 170, 682, 426, 938,
+106, 618, 362, 874, 234, 746, 490, 1002, 26, 538, 282, 794, 154, 666, 410,
+922, 90, 602, 346, 858, 218, 730, 474, 986, 58, 570, 314, 826, 186, 698,
+442, 954, 122, 634, 378, 890, 250, 762, 506, 1018, 6, 518, 262, 774, 134,
+646, 390, 902, 70, 582, 326, 838, 198, 710, 454, 966, 38, 550, 294, 806,
+166, 678, 422, 934, 102, 614, 358, 870, 230, 742, 486, 998, 22, 534, 278,
+790, 150, 662, 406, 918, 86, 598, 342, 854, 214, 726, 470, 982, 54, 566,
+310, 822, 182, 694, 438, 950, 118, 630, 374, 886, 246, 758, 502, 1014,
+14, 526, 270, 782, 142, 654, 398, 910, 78, 590, 334, 846, 206, 718, 462,
+974, 46, 558, 302, 814, 174, 686, 430, 942, 110, 622, 366, 878, 238, 750,
+494, 1006, 30, 542, 286, 798, 158, 670, 414, 926, 94, 606, 350, 862, 222,
+734, 478, 990, 62, 574, 318, 830, 190, 702, 446, 958, 126, 638, 382, 894,
+254, 766, 510, 1022, 1, 513, 257, 769, 129, 641, 385, 897, 65, 577, 321,
+833, 193, 705, 449, 961, 33, 545, 289, 801, 161, 673, 417, 929, 97, 609,
+353, 865, 225, 737, 481, 993, 17, 529, 273, 785, 145, 657, 401, 913, 81,
+593, 337, 849, 209, 721, 465, 977, 49, 561, 305, 817, 177, 689, 433, 945,
+113, 625, 369, 881, 241, 753, 497, 1009, 9, 521, 265, 777, 137, 649, 393,
+905, 73, 585, 329, 841, 201, 713, 457, 969, 41, 553, 297, 809, 169, 681,
+425, 937, 105, 617, 361, 873, 233, 745, 489, 1001, 25, 537, 281, 793, 153,
+665, 409, 921, 89, 601, 345, 857, 217, 729, 473, 985, 57, 569, 313, 825,
+185, 697, 441, 953, 121, 633, 377, 889, 249, 761, 505, 1017, 5, 517, 261,
+773, 133, 645, 389, 901, 69, 581, 325, 837, 197, 709, 453, 965, 37, 549,
+293, 805, 165, 677, 421, 933, 101, 613, 357, 869, 229, 741, 485, 997, 21,
+533, 277, 789, 149, 661, 405, 917, 85, 597, 341, 853, 213, 725, 469, 981,
+53, 565, 309, 821, 181, 693, 437, 949, 117, 629, 373, 885, 245, 757, 501,
+1013, 13, 525, 269, 781, 141, 653, 397, 909, 77, 589, 333, 845, 205, 717,
+461, 973, 45, 557, 301, 813, 173, 685, 429, 941, 109, 621, 365, 877, 237,
+749, 493, 1005, 29, 541, 285, 797, 157, 669, 413, 925, 93, 605, 349, 861,
+221, 733, 477, 989, 61, 573, 317, 829, 189, 701, 445, 957, 125, 637, 381,
+893, 253, 765, 509, 1021, 3, 515, 259, 771, 131, 643, 387, 899, 67, 579,
+323, 835, 195, 707, 451, 963, 35, 547, 291, 803, 163, 675, 419, 931, 99,
+611, 355, 867, 227, 739, 483, 995, 19, 531, 275, 787, 147, 659, 403, 915,
+83, 595, 339, 851, 211, 723, 467, 979, 51, 563, 307, 819, 179, 691, 435,
+947, 115, 627, 371, 883, 243, 755, 499, 1011, 11, 523, 267, 779, 139, 651,
+395, 907, 75, 587, 331, 843, 203, 715, 459, 971, 43, 555, 299, 811, 171,
+683, 427, 939, 107, 619, 363, 875, 235, 747, 491, 1003, 27, 539, 283, 795,
+155, 667, 411, 923, 91, 603, 347, 859, 219, 731, 475, 987, 59, 571, 315,
+827, 187, 699, 443, 955, 123, 635, 379, 891, 251, 763, 507, 1019, 7, 519,
+263, 775, 135, 647, 391, 903, 71, 583, 327, 839, 199, 711, 455, 967, 39,
+551, 295, 807, 167, 679, 423, 935, 103, 615, 359, 871, 231, 743, 487, 999,
+23, 535, 279, 791, 151, 663, 407, 919, 87, 599, 343, 855, 215, 727, 471,
+983, 55, 567, 311, 823, 183, 695, 439, 951, 119, 631, 375, 887, 247, 759,
+503, 1015, 15, 527, 271, 783, 143, 655, 399, 911, 79, 591, 335, 847, 207,
+719, 463, 975, 47, 559, 303, 815, 175, 687, 431, 943, 111, 623, 367, 879,
+239, 751, 495, 1007, 31, 543, 287, 799, 159, 671, 415, 927, 95, 607, 351,
+863, 223, 735, 479, 991, 63, 575, 319, 831, 191, 703, 447, 959, 127, 639,
+383, 895, 255, 767, 511, 1023};
+
+int Soloud_fft_bitrev_8[256] = {
+0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
+8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
+4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
+12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
+2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
+10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
+6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
+14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
+1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
+9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
+5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
+13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
+3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
+11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
+7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
+15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
+};
+
+float Soloud_fft_trig_10[508] = {
+1.000000000000000000f, 0.923879504203796390f, 0.707106769084930420f,
+0.382683396339416500f, 1.000000000000000000f, 0.980785250663757320f,
+0.923879504203796390f, 0.831469595432281490f, 0.707106769084930420f,
+0.555570185184478760f, 0.382683396339416500f, 0.195090278983116150f,
+1.000000000000000000f, 0.995184719562530520f, 0.980785250663757320f,
+0.956940352916717530f, 0.923879504203796390f, 0.881921231746673580f,
+0.831469595432281490f, 0.773010432720184330f, 0.707106769084930420f,
+0.634393274784088130f, 0.555570185184478760f, 0.471396714448928830f,
+0.382683396339416500f, 0.290284633636474610f, 0.195090278983116150f,
+0.098017096519470215f, 1.000000000000000000f, 0.998795449733734130f,
+0.995184719562530520f, 0.989176511764526370f, 0.980785250663757320f,
+0.970031261444091800f, 0.956940352916717530f, 0.941544055938720700f,
+0.923879504203796390f, 0.903989315032958980f, 0.881921231746673580f,
+0.857728600502014160f, 0.831469595432281490f, 0.803207516670227050f,
+0.773010432720184330f, 0.740951120853424070f, 0.707106769084930420f,
+0.671558916568756100f, 0.634393274784088130f, 0.595699310302734380f,
+0.555570185184478760f, 0.514102697372436520f, 0.471396714448928830f,
+0.427555054426193240f, 0.382683396339416500f, 0.336889833211898800f,
+0.290284633636474610f, 0.242980137467384340f, 0.195090278983116150f,
+0.146730437874794010f, 0.098017096519470215f, 0.049067631363868713f,
+1.000000000000000000f, 0.999698817729949950f, 0.998795449733734130f,
+0.997290432453155520f, 0.995184719562530520f, 0.992479562759399410f,
+0.989176511764526370f, 0.985277652740478520f, 0.980785250663757320f,
+0.975702106952667240f, 0.970031261444091800f, 0.963776051998138430f,
+0.956940352916717530f, 0.949528157711029050f, 0.941544055938720700f,
+0.932992815971374510f, 0.923879504203796390f, 0.914209723472595210f,
+0.903989315032958980f, 0.893224298954010010f, 0.881921231746673580f,
+0.870086967945098880f, 0.857728600502014160f, 0.844853579998016360f,
+0.831469595432281490f, 0.817584812641143800f, 0.803207516670227050f,
+0.788346409797668460f, 0.773010432720184330f, 0.757208824157714840f,
+0.740951120853424070f, 0.724247097969055180f, 0.707106769084930420f,
+0.689540505409240720f, 0.671558916568756100f, 0.653172850608825680f,
+0.634393274784088130f, 0.615231573581695560f, 0.595699310302734380f,
+0.575808167457580570f, 0.555570185184478760f, 0.534997582435607910f,
+0.514102697372436520f, 0.492898166179656980f, 0.471396714448928830f,
+0.449611306190490720f, 0.427555054426193240f, 0.405241280794143680f,
+0.382683396339416500f, 0.359894990921020510f, 0.336889833211898800f,
+0.313681721687316890f, 0.290284633636474610f, 0.266712725162506100f,
+0.242980137467384340f, 0.219101205468177800f, 0.195090278983116150f,
+0.170961856842041020f, 0.146730437874794010f, 0.122410632669925690f,
+0.098017096519470215f, 0.073564521968364716f, 0.049067631363868713f,
+0.024541186168789864f, 1.000000000000000000f, 0.999924719333648680f,
+0.999698817729949950f, 0.999322354793548580f, 0.998795449733734130f,
+0.998118102550506590f, 0.997290432453155520f, 0.996312618255615230f,
+0.995184719562530520f, 0.993906974792480470f, 0.992479562759399410f,
+0.990902662277221680f, 0.989176511764526370f, 0.987301409244537350f,
+0.985277652740478520f, 0.983105480670928960f, 0.980785250663757320f,
+0.978317379951477050f, 0.975702106952667240f, 0.972939968109130860f,
+0.970031261444091800f, 0.966976463794708250f, 0.963776051998138430f,
+0.960430502891540530f, 0.956940352916717530f, 0.953306019306182860f,
+0.949528157711029050f, 0.945607304573059080f, 0.941544055938720700f,
+0.937339007854461670f, 0.932992815971374510f, 0.928506076335906980f,
+0.923879504203796390f, 0.919113874435424800f, 0.914209723472595210f,
+0.909168004989624020f, 0.903989315032958980f, 0.898674488067626950f,
+0.893224298954010010f, 0.887639641761779790f, 0.881921231746673580f,
+0.876070082187652590f, 0.870086967945098880f, 0.863972842693328860f,
+0.857728600502014160f, 0.851355195045471190f, 0.844853579998016360f,
+0.838224709033966060f, 0.831469595432281490f, 0.824589312076568600f,
+0.817584812641143800f, 0.810457170009613040f, 0.803207516670227050f,
+0.795836865901947020f, 0.788346409797668460f, 0.780737221240997310f,
+0.773010432720184330f, 0.765167236328125000f, 0.757208824157714840f,
+0.749136388301849370f, 0.740951120853424070f, 0.732654273509979250f,
+0.724247097969055180f, 0.715730786323547360f, 0.707106769084930420f,
+0.698376238346099850f, 0.689540505409240720f, 0.680601000785827640f,
+0.671558916568756100f, 0.662415742874145510f, 0.653172850608825680f,
+0.643831551074981690f, 0.634393274784088130f, 0.624859452247619630f,
+0.615231573581695560f, 0.605511009693145750f, 0.595699310302734380f,
+0.585797846317291260f, 0.575808167457580570f, 0.565731763839721680f,
+0.555570185184478760f, 0.545324981212615970f, 0.534997582435607910f,
+0.524589657783508300f, 0.514102697372436520f, 0.503538370132446290f,
+0.492898166179656980f, 0.482183754444122310f, 0.471396714448928830f,
+0.460538685321807860f, 0.449611306190490720f, 0.438616216182708740f,
+0.427555054426193240f, 0.416429519653320310f, 0.405241280794143680f,
+0.393992006778717040f, 0.382683396339416500f, 0.371317178010940550f,
+0.359894990921020510f, 0.348418653011322020f, 0.336889833211898800f,
+0.325310260057449340f, 0.313681721687316890f, 0.302005916833877560f,
+0.290284633636474610f, 0.278519660234451290f, 0.266712725162506100f,
+0.254865616559982300f, 0.242980137467384340f, 0.231058076024055480f,
+0.219101205468177800f, 0.207111343741416930f, 0.195090278983116150f,
+0.183039844036102290f, 0.170961856842041020f, 0.158858105540275570f,
+0.146730437874794010f, 0.134580671787261960f, 0.122410632669925690f,
+0.110222168266773220f, 0.098017096519470215f, 0.085797272622585297f,
+0.073564521968364716f, 0.061320696026086807f, 0.049067631363868713f,
+0.036807179450988770f, 0.024541186168789864f, 0.012271494604647160f,
+1.000000000000000000f, 0.999981164932250980f, 0.999924719333648680f,
+0.999830603599548340f, 0.999698817729949950f, 0.999529421329498290f,
+0.999322354793548580f, 0.999077737331390380f, 0.998795449733734130f,
+0.998475551605224610f, 0.998118102550506590f, 0.997723042964935300f,
+0.997290432453155520f, 0.996820271015167240f, 0.996312618255615230f,
+0.995767414569854740f, 0.995184719562530520f, 0.994564592838287350f,
+0.993906974792480470f, 0.993211925029754640f, 0.992479562759399410f,
+0.991709768772125240f, 0.990902662277221680f, 0.990058183670043950f,
+0.989176511764526370f, 0.988257586956024170f, 0.987301409244537350f,
+0.986308097839355470f, 0.985277652740478520f, 0.984210073947906490f,
+0.983105480670928960f, 0.981963872909545900f, 0.980785250663757320f,
+0.979569792747497560f, 0.978317379951477050f, 0.977028131484985350f,
+0.975702106952667240f, 0.974339365959167480f, 0.972939968109130860f,
+0.971503913402557370f, 0.970031261444091800f, 0.968522071838378910f,
+0.966976463794708250f, 0.965394437313079830f, 0.963776051998138430f,
+0.962121427059173580f, 0.960430502891540530f, 0.958703458309173580f,
+0.956940352916717530f, 0.955141186714172360f, 0.953306019306182860f,
+0.951435029506683350f, 0.949528157711029050f, 0.947585582733154300f,
+0.945607304573059080f, 0.943593442440032960f, 0.941544055938720700f,
+0.939459204673767090f, 0.937339007854461670f, 0.935183525085449220f,
+0.932992815971374510f, 0.930766940116882320f, 0.928506076335906980f,
+0.926210224628448490f, 0.923879504203796390f, 0.921514034271240230f,
+0.919113874435424800f, 0.916679084300994870f, 0.914209723472595210f,
+0.911706030368804930f, 0.909168004989624020f, 0.906595706939697270f,
+0.903989315032958980f, 0.901348829269409180f, 0.898674488067626950f,
+0.895966231822967530f, 0.893224298954010010f, 0.890448689460754390f,
+0.887639641761779790f, 0.884797096252441410f, 0.881921231746673580f,
+0.879012227058410640f, 0.876070082187652590f, 0.873094975948333740f,
+0.870086967945098880f, 0.867046236991882320f, 0.863972842693328860f,
+0.860866904258728030f, 0.857728600502014160f, 0.854557991027832030f,
+0.851355195045471190f, 0.848120331764221190f, 0.844853579998016360f,
+0.841554939746856690f, 0.838224709033966060f, 0.834862887859344480f,
+0.831469595432281490f, 0.828045010566711430f, 0.824589312076568600f,
+0.821102499961853030f, 0.817584812641143800f, 0.814036309719085690f,
+0.810457170009613040f, 0.806847572326660160f, 0.803207516670227050f,
+0.799537241458892820f, 0.795836865901947020f, 0.792106568813323970f,
+0.788346409797668460f, 0.784556567668914790f, 0.780737221240997310f,
+0.776888430118560790f, 0.773010432720184330f, 0.769103348255157470f,
+0.765167236328125000f, 0.761202394962310790f, 0.757208824157714840f,
+0.753186762332916260f, 0.749136388301849370f, 0.745057761669158940f,
+0.740951120853424070f, 0.736816525459289550f, 0.732654273509979250f,
+0.728464365005493160f, 0.724247097969055180f, 0.720002472400665280f,
+0.715730786323547360f, 0.711432158946990970f, 0.707106769084930420f,
+0.702754735946655270f, 0.698376238346099850f, 0.693971455097198490f,
+0.689540505409240720f, 0.685083627700805660f, 0.680601000785827640f,
+0.676092684268951420f, 0.671558916568756100f, 0.666999876499176030f,
+0.662415742874145510f, 0.657806694507598880f, 0.653172850608825680f,
+0.648514389991760250f, 0.643831551074981690f, 0.639124453067779540f,
+0.634393274784088130f, 0.629638195037841800f, 0.624859452247619630f,
+0.620057165622711180f, 0.615231573581695560f, 0.610382795333862300f,
+0.605511009693145750f, 0.600616455078125000f, 0.595699310302734380f,
+0.590759694576263430f, 0.585797846317291260f, 0.580813944339752200f,
+0.575808167457580570f, 0.570780694484710690f, 0.565731763839721680f,
+0.560661554336547850f, 0.555570185184478760f, 0.550457954406738280f,
+0.545324981212615970f, 0.540171444416046140f, 0.534997582435607910f,
+0.529803574085235600f, 0.524589657783508300f, 0.519355952739715580f,
+0.514102697372436520f, 0.508830130100250240f, 0.503538370132446290f,
+0.498227655887603760f, 0.492898166179656980f, 0.487550139427185060f,
+0.482183754444122310f, 0.476799190044403080f, 0.471396714448928830f,
+0.465976476669311520f, 0.460538685321807860f, 0.455083549022674560f,
+0.449611306190490720f, 0.444122105836868290f, 0.438616216182708740f,
+0.433093786239624020f, 0.427555054426193240f, 0.422000229358673100f,
+0.416429519653320310f, 0.410843133926391600f, 0.405241280794143680f,
+0.399624168872833250f, 0.393992006778717040f, 0.388345003128051760f,
+0.382683396339416500f, 0.377007365226745610f, 0.371317178010940550f,
+0.365612953901290890f, 0.359894990921020510f, 0.354163497686386110f,
+0.348418653011322020f, 0.342660695314407350f, 0.336889833211898800f,
+0.331106275320053100f, 0.325310260057449340f, 0.319501996040344240f,
+0.313681721687316890f, 0.307849615812301640f, 0.302005916833877560f,
+0.296150863170623780f, 0.290284633636474610f, 0.284407496452331540f,
+0.278519660234451290f, 0.272621333599090580f, 0.266712725162506100f,
+0.260794073343276980f, 0.254865616559982300f, 0.248927563428878780f,
+0.242980137467384340f, 0.237023577094078060f, 0.231058076024055480f,
+0.225083872675895690f, 0.219101205468177800f, 0.213110283017158510f,
+0.207111343741416930f, 0.201104596257209780f, 0.195090278983116150f,
+0.189068630337715150f, 0.183039844036102290f, 0.177004188299179080f,
+0.170961856842041020f, 0.164913088083267210f, 0.158858105540275570f,
+0.152797147631645200f, 0.146730437874794010f, 0.140658199787139890f,
+0.134580671787261960f, 0.128498077392578130f, 0.122410632669925690f,
+0.116318590939044950f, 0.110222168266773220f, 0.104121595621109010f,
+0.098017096519470215f, 0.091908916831016541f, 0.085797272622585297f,
+0.079682394862174988f, 0.073564521968364716f, 0.067443877458572388f,
+0.061320696026086807f, 0.055195201188325882f, 0.049067631363868713f,
+0.042938213795423508f, 0.036807179450988770f, 0.030674761161208153f,
+0.024541186168789864f, 0.018406687304377556f, 0.012271494604647160f,
+0.006135840900242329f
+};
+
+float Soloud_fft_trig_8[124] = {
+1.000000000000000000f, 0.923879504203796390f, 0.707106769084930420f,
+0.382683396339416500f, 1.000000000000000000f, 0.980785250663757320f,
+0.923879504203796390f, 0.831469595432281490f, 0.707106769084930420f,
+0.555570185184478760f, 0.382683396339416500f, 0.195090278983116150f,
+1.000000000000000000f, 0.995184719562530520f, 0.980785250663757320f,
+0.956940352916717530f, 0.923879504203796390f, 0.881921231746673580f,
+0.831469595432281490f, 0.773010432720184330f, 0.707106769084930420f,
+0.634393274784088130f, 0.555570185184478760f, 0.471396714448928830f,
+0.382683396339416500f, 0.290284633636474610f, 0.195090278983116150f,
+0.098017096519470215f, 1.000000000000000000f, 0.998795449733734130f,
+0.995184719562530520f, 0.989176511764526370f, 0.980785250663757320f,
+0.970031261444091800f, 0.956940352916717530f, 0.941544055938720700f,
+0.923879504203796390f, 0.903989315032958980f, 0.881921231746673580f,
+0.857728600502014160f, 0.831469595432281490f, 0.803207516670227050f,
+0.773010432720184330f, 0.740951120853424070f, 0.707106769084930420f,
+0.671558916568756100f, 0.634393274784088130f, 0.595699310302734380f,
+0.555570185184478760f, 0.514102697372436520f, 0.471396714448928830f,
+0.427555054426193240f, 0.382683396339416500f, 0.336889833211898800f,
+0.290284633636474610f, 0.242980137467384340f, 0.195090278983116150f,
+0.146730437874794010f, 0.098017096519470215f, 0.049067631363868713f,
+1.000000000000000000f, 0.999698817729949950f, 0.998795449733734130f,
+0.997290432453155520f, 0.995184719562530520f, 0.992479562759399410f,
+0.989176511764526370f, 0.985277652740478520f, 0.980785250663757320f,
+0.975702106952667240f, 0.970031261444091800f, 0.963776051998138430f,
+0.956940352916717530f, 0.949528157711029050f, 0.941544055938720700f,
+0.932992815971374510f, 0.923879504203796390f, 0.914209723472595210f,
+0.903989315032958980f, 0.893224298954010010f, 0.881921231746673580f,
+0.870086967945098880f, 0.857728600502014160f, 0.844853579998016360f,
+0.831469595432281490f, 0.817584812641143800f, 0.803207516670227050f,
+0.788346409797668460f, 0.773010432720184330f, 0.757208824157714840f,
+0.740951120853424070f, 0.724247097969055180f, 0.707106769084930420f,
+0.689540505409240720f, 0.671558916568756100f, 0.653172850608825680f,
+0.634393274784088130f, 0.615231573581695560f, 0.595699310302734380f,
+0.575808167457580570f, 0.555570185184478760f, 0.534997582435607910f,
+0.514102697372436520f, 0.492898166179656980f, 0.471396714448928830f,
+0.449611306190490720f, 0.427555054426193240f, 0.405241280794143680f,
+0.382683396339416500f, 0.359894990921020510f, 0.336889833211898800f,
+0.313681721687316890f, 0.290284633636474610f, 0.266712725162506100f,
+0.242980137467384340f, 0.219101205468177800f, 0.195090278983116150f,
+0.170961856842041020f, 0.146730437874794010f, 0.122410632669925690f,
+0.098017096519470215f, 0.073564521968364716f, 0.049067631363868713f,
+0.024541186168789864f
+};
+
diff --git a/src/soloud/src/core/soloud_file.cpp b/src/soloud/src/core/soloud_file.cpp
new file mode 100644
index 0000000..5568fce
--- /dev/null
+++ b/src/soloud/src/core/soloud_file.cpp
@@ -0,0 +1,305 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. 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
+appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#undef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+
+#include
+#include
+#include "soloud.h"
+#include "soloud_file.h"
+
+namespace SoLoud
+{
+ unsigned int File::read8()
+ {
+ unsigned char d = 0;
+ read((unsigned char*)&d, 1);
+ return d;
+ }
+
+ unsigned int File::read16()
+ {
+ unsigned short d = 0;
+ read((unsigned char*)&d, 2);
+ return d;
+ }
+
+ unsigned int File::read32()
+ {
+ unsigned int d = 0;
+ read((unsigned char*)&d, 4);
+ return d;
+ }
+
+DiskFile::DiskFile(FILE *fp):
+mFileHandle(fp)
+{
+
+}
+
+ unsigned int DiskFile::read(unsigned char *aDst, unsigned int aBytes)
+ {
+ return (unsigned int)fread(aDst, 1, aBytes, mFileHandle);
+ }
+
+ unsigned int DiskFile::length()
+ {
+ if (!mFileHandle)
+ return 0;
+ int pos = ftell(mFileHandle);
+ fseek(mFileHandle, 0, SEEK_END);
+ int len = ftell(mFileHandle);
+ fseek(mFileHandle, pos, SEEK_SET);
+ return len;
+ }
+
+ void DiskFile::seek(int aOffset)
+ {
+ fseek(mFileHandle, aOffset, SEEK_SET);
+ }
+
+ unsigned int DiskFile::pos()
+ {
+ return ftell(mFileHandle);
+ }
+
+ FILE *DiskFile::getFilePtr()
+ {
+ return mFileHandle;
+ }
+
+ DiskFile::~DiskFile()
+ {
+ if (mFileHandle)
+ fclose(mFileHandle);
+ }
+
+ DiskFile::DiskFile()
+ {
+ mFileHandle = 0;
+ }
+
+ result DiskFile::open(const char *aFilename)
+ {
+ if (!aFilename)
+ return INVALID_PARAMETER;
+ mFileHandle = fopen(aFilename, "rb");
+ if (!mFileHandle)
+ return FILE_NOT_FOUND;
+ return SO_NO_ERROR;
+ }
+
+ int DiskFile::eof()
+ {
+ return feof(mFileHandle);
+ }
+
+
+
+ unsigned int MemoryFile::read(unsigned char *aDst, unsigned int aBytes)
+ {
+ if (mOffset + aBytes >= mDataLength)
+ aBytes = mDataLength - mOffset;
+
+ memcpy(aDst, mDataPtr + mOffset, aBytes);
+ mOffset += aBytes;
+
+ return aBytes;
+ }
+
+ unsigned int MemoryFile::length()
+ {
+ return mDataLength;
+ }
+
+ void MemoryFile::seek(int aOffset)
+ {
+ if (aOffset >= 0)
+ mOffset = aOffset;
+ else
+ mOffset = mDataLength + aOffset;
+ if (mOffset > mDataLength-1)
+ mOffset = mDataLength-1;
+ }
+
+ unsigned int MemoryFile::pos()
+ {
+ return mOffset;
+ }
+
+ unsigned char * MemoryFile::getMemPtr()
+ {
+ return mDataPtr;
+ }
+
+ MemoryFile::~MemoryFile()
+ {
+ if (mDataOwned)
+ delete[] mDataPtr;
+ }
+
+ MemoryFile::MemoryFile()
+ {
+ mDataPtr = 0;
+ mDataLength = 0;
+ mOffset = 0;
+ mDataOwned = false;
+ }
+
+ result MemoryFile::openMem(unsigned char *aData, unsigned int aDataLength, bool aCopy, bool aTakeOwnership)
+ {
+ if (aData == NULL || aDataLength == 0)
+ return INVALID_PARAMETER;
+
+ if (mDataOwned)
+ delete[] mDataPtr;
+ mDataPtr = 0;
+ mOffset = 0;
+
+ mDataLength = aDataLength;
+
+ if (aCopy)
+ {
+ mDataOwned = true;
+ mDataPtr = new unsigned char[aDataLength];
+ if (mDataPtr == NULL)
+ return OUT_OF_MEMORY;
+ memcpy(mDataPtr, aData, aDataLength);
+ return SO_NO_ERROR;
+ }
+
+ mDataPtr = aData;
+ mDataOwned = aTakeOwnership;
+ return SO_NO_ERROR;
+ }
+
+ result MemoryFile::openToMem(const char *aFile)
+ {
+ if (!aFile)
+ return INVALID_PARAMETER;
+ if (mDataOwned)
+ delete[] mDataPtr;
+ mDataPtr = 0;
+ mOffset = 0;
+
+ DiskFile df;
+ int res = df.open(aFile);
+ if (res != SO_NO_ERROR)
+ return res;
+
+ mDataLength = df.length();
+ mDataPtr = new unsigned char[mDataLength];
+ if (mDataPtr == NULL)
+ return OUT_OF_MEMORY;
+ df.read(mDataPtr, mDataLength);
+ mDataOwned = true;
+ return SO_NO_ERROR;
+ }
+
+ result MemoryFile::openFileToMem(File *aFile)
+ {
+ if (!aFile)
+ return INVALID_PARAMETER;
+ if (mDataOwned)
+ delete[] mDataPtr;
+ mDataPtr = 0;
+ mOffset = 0;
+
+ mDataLength = aFile->length();
+ mDataPtr = new unsigned char[mDataLength];
+ if (mDataPtr == NULL)
+ return OUT_OF_MEMORY;
+ aFile->read(mDataPtr, mDataLength);
+ mDataOwned = true;
+ return SO_NO_ERROR;
+ }
+
+ int MemoryFile::eof()
+ {
+ if (mOffset >= mDataLength)
+ return 1;
+ return 0;
+ }
+}
+
+extern "C"
+{
+ int Soloud_Filehack_fgetc(Soloud_Filehack *f)
+ {
+ SoLoud::File *fp = (SoLoud::File *)f;
+ if (fp->eof())
+ return EOF;
+ return fp->read8();
+ }
+
+ int Soloud_Filehack_fread(void *dst, int s, int c, Soloud_Filehack *f)
+ {
+ SoLoud::File *fp = (SoLoud::File *)f;
+ return fp->read((unsigned char*)dst, s*c) / s;
+
+ }
+
+ int Soloud_Filehack_fseek(Soloud_Filehack *f, int idx, int base)
+ {
+ SoLoud::File *fp = (SoLoud::File *)f;
+ switch (base)
+ {
+ case SEEK_CUR:
+ fp->seek(fp->pos() + idx);
+ break;
+ case SEEK_END:
+ fp->seek(fp->length() + idx);
+ break;
+ default:
+ fp->seek(idx);
+ }
+ return 0;
+ }
+
+ int Soloud_Filehack_ftell(Soloud_Filehack *f)
+ {
+ SoLoud::File *fp = (SoLoud::File *)f;
+ return fp->pos();
+ }
+
+ int Soloud_Filehack_fclose(Soloud_Filehack *f)
+ {
+ SoLoud::File *fp = (SoLoud::File *)f;
+ delete fp;
+ return 0;
+ }
+
+ Soloud_Filehack * Soloud_Filehack_fopen(const char *aFilename, char *aMode)
+ {
+ SoLoud::DiskFile *df = new SoLoud::DiskFile();
+ int res = df->open(aFilename);
+ if (res != SoLoud::SO_NO_ERROR)
+ {
+ delete df;
+ df = 0;
+ }
+ return (Soloud_Filehack*)df;
+ }
+}
diff --git a/src/soloud/src/core/soloud_filter.cpp b/src/soloud/src/core/soloud_filter.cpp
new file mode 100644
index 0000000..0ae5679
--- /dev/null
+++ b/src/soloud/src/core/soloud_filter.cpp
@@ -0,0 +1,142 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+
+ Filter::Filter()
+ {
+ }
+
+ Filter::~Filter()
+ {
+ }
+
+ FilterInstance::FilterInstance()
+ {
+ mNumParams = 0;
+ mParamChanged = 0;
+ mParam = 0;
+ mParamFader = 0;
+ }
+
+ result FilterInstance::initParams(int aNumParams)
+ {
+ mNumParams = aNumParams;
+ delete[] mParam;
+ delete[] mParamFader;
+ mParam = new float[mNumParams];
+ mParamFader = new Fader[mNumParams];
+
+ if (mParam == NULL || mParamFader == NULL)
+ {
+ delete[] mParam;
+ delete[] mParamFader;
+ mParam = NULL;
+ mParamFader = NULL;
+ mNumParams = 0;
+ return OUT_OF_MEMORY;
+ }
+
+ unsigned int i;
+ for (i = 0; i < mNumParams; i++)
+ {
+ mParam[i] = 0;
+ mParamFader[i].mActive = 0;
+ }
+ mParam[0] = 1; // set 'wet' to 1
+
+ return 0;
+ }
+
+ void FilterInstance::updateParams(double aTime)
+ {
+ unsigned int i;
+ for (i = 0; i < mNumParams; i++)
+ {
+ if (mParamFader[i].mActive > 0)
+ {
+ mParamChanged |= 1 << i;
+ mParam[i] = mParamFader[i].get(aTime);
+ }
+ }
+ }
+
+ FilterInstance::~FilterInstance()
+ {
+ delete[] mParam;
+ delete[] mParamFader;
+ }
+
+ void FilterInstance::setFilterParameter(unsigned int aAttributeId, float aValue)
+ {
+ if (aAttributeId >= mNumParams)
+ return;
+
+ mParamFader[aAttributeId].mActive = 0;
+ mParam[aAttributeId] = aValue;
+ mParamChanged |= 1 << aAttributeId;
+ }
+
+ void FilterInstance::fadeFilterParameter(unsigned int aAttributeId, float aTo, double aTime, double aStartTime)
+ {
+ if (aAttributeId >= mNumParams || aTime <= 0 || aTo == mParam[aAttributeId])
+ return;
+
+ mParamFader[aAttributeId].set(mParam[aAttributeId], aTo, aTime, aStartTime);
+ }
+
+ void FilterInstance::oscillateFilterParameter(unsigned int aAttributeId, float aFrom, float aTo, double aTime, double aStartTime)
+ {
+ if (aAttributeId >= mNumParams || aTime <= 0 || aFrom == aTo)
+ return;
+
+ mParamFader[aAttributeId].setLFO(aFrom, aTo, aTime, aStartTime);
+ }
+
+ float FilterInstance::getFilterParameter(unsigned int aAttributeId)
+ {
+ if (aAttributeId >= mNumParams)
+ return 0;
+
+ return mParam[aAttributeId];
+ }
+
+ void FilterInstance::filter(float *aBuffer, unsigned int aSamples, unsigned int aChannels, float aSamplerate, double aTime)
+ {
+ unsigned int i;
+ for (i = 0; i < aChannels; i++)
+ {
+ filterChannel(aBuffer + i * aSamples, aSamples, aSamplerate, aTime, i, aChannels);
+ }
+ }
+
+ void FilterInstance::filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, double aTime, unsigned int aChannel, unsigned int aChannels)
+ {
+ }
+
+};
+
diff --git a/src/soloud/src/core/soloud_queue.cpp b/src/soloud/src/core/soloud_queue.cpp
new file mode 100644
index 0000000..9f9ce4f
--- /dev/null
+++ b/src/soloud/src/core/soloud_queue.cpp
@@ -0,0 +1,176 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+
+namespace SoLoud
+{
+ QueueInstance::QueueInstance(Queue *aParent)
+ {
+ mParent = aParent;
+ mFlags |= PROTECTED;
+ }
+
+ unsigned int QueueInstance::getAudio(float *aBuffer, unsigned int aSamplesToRead, unsigned int aBufferSize)
+ {
+ if (mParent->mCount == 0)
+ {
+ return 0;
+ }
+ unsigned int copycount = aSamplesToRead;
+ unsigned int copyofs = 0;
+ while (copycount && mParent->mCount)
+ {
+ int readcount = mParent->mSource[mParent->mReadIndex]->getAudio(aBuffer + copyofs, copycount, aBufferSize);
+ copyofs += readcount;
+ copycount -= readcount;
+ if (mParent->mSource[mParent->mReadIndex]->hasEnded())
+ {
+ delete mParent->mSource[mParent->mReadIndex];
+ mParent->mSource[mParent->mReadIndex] = 0;
+ mParent->mReadIndex = (mParent->mReadIndex + 1) % SOLOUD_QUEUE_MAX;
+ mParent->mCount--;
+ }
+ }
+ return copyofs;
+ }
+
+ bool QueueInstance::hasEnded()
+ {
+ return mParent->mCount == 0;
+ }
+
+ QueueInstance::~QueueInstance()
+ {
+ }
+
+ Queue::Queue()
+ {
+ mQueueHandle = 0;
+ mInstance = 0;
+ mReadIndex = 0;
+ mWriteIndex = 0;
+ mCount = 0;
+ }
+
+ QueueInstance * Queue::createInstance()
+ {
+ if (mInstance)
+ {
+ stop();
+ mInstance = 0;
+ }
+ mInstance = new QueueInstance(this);
+ return mInstance;
+ }
+
+ void Queue::findQueueHandle()
+ {
+ // Find the channel the queue is playing on to calculate handle..
+ int i;
+ for (i = 0; mQueueHandle == 0 && i < (signed)mSoloud->mHighestVoice; i++)
+ {
+ if (mSoloud->mVoice[i] == mInstance)
+ {
+ mQueueHandle = mSoloud->getHandleFromVoice(i);
+ }
+ }
+ }
+
+ result Queue::play(AudioSource &aSound)
+ {
+ if (!mSoloud)
+ {
+ return INVALID_PARAMETER;
+ }
+
+ findQueueHandle();
+
+ if (mQueueHandle == 0)
+ return INVALID_PARAMETER;
+
+ if (mCount >= SOLOUD_QUEUE_MAX)
+ return OUT_OF_MEMORY;
+
+ if (!aSound.mAudioSourceID)
+ {
+ aSound.mAudioSourceID = mSoloud->mAudioSourceID;
+ mSoloud->mAudioSourceID++;
+ }
+
+ SoLoud::AudioSourceInstance *instance = aSound.createInstance();
+
+ if (instance == 0)
+ {
+ return OUT_OF_MEMORY;
+ }
+
+ instance->mAudioSourceID = aSound.mAudioSourceID;
+
+ mSoloud->lockAudioMutex();
+ mSource[mWriteIndex] = instance;
+ mWriteIndex = (mWriteIndex + 1) % SOLOUD_QUEUE_MAX;
+ mCount++;
+ mSoloud->unlockAudioMutex();
+
+ return SO_NO_ERROR;
+ }
+
+
+ unsigned int Queue::getQueueCount()
+ {
+ unsigned int count;
+ mSoloud->lockAudioMutex();
+ count = mCount;
+ mSoloud->unlockAudioMutex();
+ return count;
+ }
+
+ bool Queue::isCurrentlyPlaying(AudioSource &aSound)
+ {
+ if (mCount == 0 || aSound.mAudioSourceID == 0)
+ return false;
+ mSoloud->lockAudioMutex();
+ bool res = mSource[mReadIndex]->mAudioSourceID == aSound.mAudioSourceID;
+ mSoloud->unlockAudioMutex();
+ return res;
+ }
+
+ result Queue::setParamsFromAudioSource(AudioSource &aSound)
+ {
+ mChannels = aSound.mChannels;
+ mBaseSamplerate = aSound.mBaseSamplerate;
+
+ return SO_NO_ERROR;
+ }
+
+ result Queue::setParams(float aSamplerate, unsigned int aChannels)
+ {
+ if (aChannels < 1 || aChannels > MAX_CHANNELS)
+ return INVALID_PARAMETER;
+ mChannels = aChannels;
+ mBaseSamplerate = aSamplerate;
+ return SO_NO_ERROR;
+ }
+};
diff --git a/src/soloud/src/core/soloud_thread.cpp b/src/soloud/src/core/soloud_thread.cpp
new file mode 100644
index 0000000..8b27c61
--- /dev/null
+++ b/src/soloud/src/core/soloud_thread.cpp
@@ -0,0 +1,310 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#if defined(_WIN32)||defined(_WIN64)
+#include
+#else
+#include
+#include
+#endif
+
+#include "soloud.h"
+#include "soloud_thread.h"
+
+namespace SoLoud
+{
+ namespace Thread
+ {
+#ifdef WINDOWS_VERSION
+ struct ThreadHandleData
+ {
+ HANDLE thread;
+ };
+
+ void * createMutex()
+ {
+ CRITICAL_SECTION * cs = new CRITICAL_SECTION;
+ InitializeCriticalSectionAndSpinCount(cs, 100);
+ return (void*)cs;
+ }
+
+ void destroyMutex(void *aHandle)
+ {
+ CRITICAL_SECTION *cs = (CRITICAL_SECTION*)aHandle;
+ DeleteCriticalSection(cs);
+ delete cs;
+ }
+
+ void lockMutex(void *aHandle)
+ {
+ CRITICAL_SECTION *cs = (CRITICAL_SECTION*)aHandle;
+ if (cs)
+ {
+ EnterCriticalSection(cs);
+ }
+ }
+
+ void unlockMutex(void *aHandle)
+ {
+ CRITICAL_SECTION *cs = (CRITICAL_SECTION*)aHandle;
+ if (cs)
+ {
+ LeaveCriticalSection(cs);
+ }
+ }
+
+ struct soloud_thread_data
+ {
+ threadFunction mFunc;
+ void *mParam;
+ };
+
+ static DWORD WINAPI threadfunc(LPVOID d)
+ {
+ soloud_thread_data *p = (soloud_thread_data *)d;
+ p->mFunc(p->mParam);
+ delete p;
+ return 0;
+ }
+
+ ThreadHandle createThread(threadFunction aThreadFunction, void *aParameter)
+ {
+ soloud_thread_data *d = new soloud_thread_data;
+ d->mFunc = aThreadFunction;
+ d->mParam = aParameter;
+ HANDLE h = CreateThread(NULL,0,threadfunc,d,0,NULL);
+ if (0 == h)
+ {
+ return 0;
+ }
+ ThreadHandleData *threadHandle = new ThreadHandleData;
+ threadHandle->thread = h;
+ return threadHandle;
+ }
+
+ void sleep(int aMSec)
+ {
+ Sleep(aMSec);
+ }
+
+ void wait(ThreadHandle aThreadHandle)
+ {
+ WaitForSingleObject(aThreadHandle->thread, INFINITE);
+ }
+
+ void release(ThreadHandle aThreadHandle)
+ {
+ CloseHandle(aThreadHandle->thread);
+ delete aThreadHandle;
+ }
+
+#else // pthreads
+ struct ThreadHandleData
+ {
+ pthread_t thread;
+ };
+
+ void * createMutex()
+ {
+ pthread_mutex_t *mutex;
+ mutex = new pthread_mutex_t;
+
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+
+ pthread_mutex_init(mutex, &attr);
+
+ return (void*)mutex;
+ }
+
+ void destroyMutex(void *aHandle)
+ {
+ pthread_mutex_t *mutex = (pthread_mutex_t*)aHandle;
+
+ if (mutex)
+ {
+ pthread_mutex_destroy(mutex);
+ delete mutex;
+ }
+ }
+
+ void lockMutex(void *aHandle)
+ {
+ pthread_mutex_t *mutex = (pthread_mutex_t*)aHandle;
+ if (mutex)
+ {
+ pthread_mutex_lock(mutex);
+ }
+ }
+
+ void unlockMutex(void *aHandle)
+ {
+ pthread_mutex_t *mutex = (pthread_mutex_t*)aHandle;
+ if (mutex)
+ {
+ pthread_mutex_unlock(mutex);
+ }
+ }
+
+ struct soloud_thread_data
+ {
+ threadFunction mFunc;
+ void *mParam;
+ };
+
+ static void * threadfunc(void * d)
+ {
+ soloud_thread_data *p = (soloud_thread_data *)d;
+ p->mFunc(p->mParam);
+ delete p;
+ return 0;
+ }
+
+ ThreadHandle createThread(threadFunction aThreadFunction, void *aParameter)
+ {
+ soloud_thread_data *d = new soloud_thread_data;
+ d->mFunc = aThreadFunction;
+ d->mParam = aParameter;
+
+ ThreadHandleData *threadHandle = new ThreadHandleData;
+ pthread_create(&threadHandle->thread, NULL, threadfunc, (void*)d);
+ return threadHandle;
+ }
+
+ void sleep(int aMSec)
+ {
+ usleep(aMSec * 1000);
+ }
+
+ void wait(ThreadHandle aThreadHandle)
+ {
+ pthread_join(aThreadHandle->thread, 0);
+ }
+
+ void release(ThreadHandle aThreadHandle)
+ {
+ delete aThreadHandle;
+ }
+#endif
+
+ static void poolWorker(void *aParam)
+ {
+ Pool *myPool = (Pool*)aParam;
+ while (myPool->mRunning)
+ {
+ PoolTask *t = myPool->getWork();
+ if (!t)
+ {
+ sleep(1);
+ }
+ else
+ {
+ t->work();
+ }
+ }
+ }
+
+ Pool::Pool()
+ {
+ mRunning = 0;
+ mThreadCount = 0;
+ mThread = 0;
+ mWorkMutex = 0;
+ mRobin = 0;
+ mMaxTask = 0;
+ }
+
+ Pool::~Pool()
+ {
+ mRunning = 0;
+ int i;
+ for (i = 0; i < mThreadCount; i++)
+ {
+ wait(mThread[i]);
+ release(mThread[i]);
+ }
+ delete[] mThread;
+ if (mWorkMutex)
+ destroyMutex(mWorkMutex);
+ }
+
+ void Pool::init(int aThreadCount)
+ {
+ if (aThreadCount > 0)
+ {
+ mMaxTask = 0;
+ mWorkMutex = createMutex();
+ mRunning = 1;
+ mThreadCount = aThreadCount;
+ mThread = new ThreadHandle[aThreadCount];
+ int i;
+ for (i = 0; i < mThreadCount; i++)
+ {
+ mThread[i] = createThread(poolWorker, this);
+ }
+ }
+ }
+
+ void Pool::addWork(PoolTask *aTask)
+ {
+ if (mThreadCount == 0)
+ {
+ aTask->work();
+ }
+ else
+ {
+ if (mWorkMutex) lockMutex(mWorkMutex);
+ if (mMaxTask == MAX_THREADPOOL_TASKS)
+ {
+ // If we're at max tasks, do the task on calling thread
+ // (we're in trouble anyway, might as well slow down adding more work)
+ if (mWorkMutex) unlockMutex(mWorkMutex);
+ aTask->work();
+ }
+ else
+ {
+ mTaskArray[mMaxTask] = aTask;
+ mMaxTask++;
+ if (mWorkMutex) unlockMutex(mWorkMutex);
+ }
+ }
+ }
+
+ PoolTask * Pool::getWork()
+ {
+ PoolTask *t = 0;
+ if (mWorkMutex) lockMutex(mWorkMutex);
+ if (mMaxTask > 0)
+ {
+ int r = mRobin % mMaxTask;
+ mRobin++;
+ t = mTaskArray[r];
+ mTaskArray[r] = mTaskArray[mMaxTask - 1];
+ mMaxTask--;
+ }
+ if (mWorkMutex) unlockMutex(mWorkMutex);
+ return t;
+ }
+ }
+}
diff --git a/src/soloud/src/filter/soloud_bassboostfilter.cpp b/src/soloud/src/filter/soloud_bassboostfilter.cpp
new file mode 100644
index 0000000..67c1707
--- /dev/null
+++ b/src/soloud/src/filter/soloud_bassboostfilter.cpp
@@ -0,0 +1,65 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include "soloud.h"
+#include "soloud_bassboostfilter.h"
+
+
+namespace SoLoud
+{
+ BassboostFilterInstance::BassboostFilterInstance(BassboostFilter *aParent)
+ {
+ mParent = aParent;
+ initParams(2);
+ mParam[BOOST] = aParent->mBoost;
+ }
+
+ void BassboostFilterInstance::fftFilterChannel(float *aFFTBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels)
+ {
+ unsigned int i;
+ for (i = 0; i < 2; i++)
+ {
+ aFFTBuffer[i * 2] *= mParam[BOOST];
+ }
+ }
+
+ result BassboostFilter::setParams(float aBoost)
+ {
+ if (aBoost < 0)
+ return INVALID_PARAMETER;
+ mBoost = aBoost;
+ return SO_NO_ERROR;
+ }
+
+ BassboostFilter::BassboostFilter()
+ {
+ mBoost = 2;
+ }
+
+ FilterInstance *BassboostFilter::createInstance()
+ {
+ return new BassboostFilterInstance(this);
+ }
+}
diff --git a/src/soloud/src/filter/soloud_biquadresonantfilter.cpp b/src/soloud/src/filter/soloud_biquadresonantfilter.cpp
new file mode 100644
index 0000000..1b7e74f
--- /dev/null
+++ b/src/soloud/src/filter/soloud_biquadresonantfilter.cpp
@@ -0,0 +1,182 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+--
+
+Based on "Using the Biquad Resonant Filter",
+Phil Burk, Game Programming Gems 3, p. 606
+*/
+
+#include
+#include
+#include "soloud.h"
+#include "soloud_biquadresonantfilter.h"
+
+namespace SoLoud
+{
+ void BiquadResonantFilterInstance::calcBQRParams()
+ {
+ mDirty = 0;
+
+ float omega = (float)((2.0f * M_PI * mParam[FREQUENCY]) / mParam[SAMPLERATE]);
+ float sin_omega = (float)sin(omega);
+ float cos_omega = (float)cos(omega);
+ float alpha = sin_omega / (2.0f * mParam[RESONANCE]);
+ float scalar = 1.0f / (1.0f + alpha);
+
+ mActive = 1;
+
+ switch (mFilterType)
+ {
+ case BiquadResonantFilter::NONE:
+ mActive = 0;
+ break;
+ case BiquadResonantFilter::LOWPASS:
+ mA0 = 0.5f * (1.0f - cos_omega) * scalar;
+ mA1 = (1.0f - cos_omega) * scalar;
+ mA2 = mA0;
+ mB1 = -2.0f * cos_omega * scalar;
+ mB2 = (1.0f - alpha) * scalar;
+ break;
+ case BiquadResonantFilter::HIGHPASS:
+ mA0 = 0.5f * (1.0f + cos_omega) * scalar;
+ mA1 = -(1.0f + cos_omega) * scalar;
+ mA2 = mA0;
+ mB1 = -2.0f * cos_omega * scalar;
+ mB2 = (1.0f - alpha) * scalar;
+ break;
+ case BiquadResonantFilter::BANDPASS:
+ mA0 = alpha * scalar;
+ mA1 = 0;
+ mA2 = -mA0;
+ mB1 = -2.0f * cos_omega * scalar;
+ mB2 = (1.0f - alpha) * scalar;
+ break;
+ }
+ }
+
+
+ BiquadResonantFilterInstance::BiquadResonantFilterInstance(BiquadResonantFilter *aParent)
+ {
+ int i;
+ for (i = 0; i < 2; i++)
+ {
+ mState[i].mX1 = 0;
+ mState[i].mY1 = 0;
+ mState[i].mX2 = 0;
+ mState[i].mY2 = 0;
+ }
+
+ mParent = aParent;
+ mFilterType = aParent->mFilterType;
+
+ initParams(4);
+
+ mParam[SAMPLERATE] = aParent->mSampleRate;
+ mParam[RESONANCE] = aParent->mResonance;
+ mParam[FREQUENCY] = aParent->mFrequency;
+ mParam[WET] = 1;
+
+ calcBQRParams();
+ }
+
+ void BiquadResonantFilterInstance::filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, double aTime, unsigned int aChannel, unsigned int aChannels)
+ {
+ if (!mActive)
+ return;
+
+ if (aChannel == 0)
+ {
+ updateParams(aTime);
+
+ if (mParamChanged & ((1 << FREQUENCY) | (1 << RESONANCE) | (1 << SAMPLERATE)))
+ {
+ calcBQRParams();
+ }
+ mParamChanged = 0;
+ }
+
+ float x;
+ unsigned int i;
+ int c = 0;
+
+ BQRStateData &s = mState[aChannel];
+
+ for (i = 0; i < aSamples; i +=2, c++)
+ {
+ // Generate outputs by filtering inputs.
+ x = aBuffer[c];
+ s.mY2 = (mA0 * x) + (mA1 * s.mX1) + (mA2 * s.mX2) - (mB1 * s.mY1) - (mB2 * s.mY2);
+ aBuffer[c] += (s.mY2 - aBuffer[c]) * mParam[WET];
+
+ c++;
+
+ // Permute filter operations to reduce data movement.
+ // Just substitute variables instead of doing mX1=x, etc.
+ s.mX2 = aBuffer[c];
+ s.mY1 = (mA0 * s.mX2) + (mA1 * x) + (mA2 * s.mX1) - (mB1 * s.mY2) - (mB2 * s.mY1);
+ aBuffer[c] += (s.mY1 - aBuffer[c]) * mParam[WET];
+
+ // Only move a little data.
+ s.mX1 = s.mX2;
+ s.mX2 = x;
+ }
+
+ // Apply a small impulse to filter to prevent arithmetic underflow,
+ // which can cause the FPU to interrupt the CPU.
+ s.mY1 += (float) 1.0E-26;
+ }
+
+
+ BiquadResonantFilterInstance::~BiquadResonantFilterInstance()
+ {
+ }
+
+ BiquadResonantFilter::BiquadResonantFilter()
+ {
+ setParams(LOWPASS, 44100, 1000, 2);
+ }
+
+ result BiquadResonantFilter::setParams(int aType, float aSampleRate, float aFrequency, float aResonance)
+ {
+ if (aType < 0 || aType > 3 || aSampleRate <= 0 || aFrequency <= 0 || aResonance <= 0)
+ return INVALID_PARAMETER;
+
+ mFilterType = aType;
+ mSampleRate = aSampleRate;
+ mFrequency = aFrequency;
+ mResonance = aResonance;
+
+ return 0;
+ }
+
+ BiquadResonantFilter::~BiquadResonantFilter()
+ {
+ }
+
+
+ BiquadResonantFilterInstance *BiquadResonantFilter::createInstance()
+ {
+ return new BiquadResonantFilterInstance(this);
+ }
+}
diff --git a/src/soloud/src/filter/soloud_dcremovalfilter.cpp b/src/soloud/src/filter/soloud_dcremovalfilter.cpp
new file mode 100644
index 0000000..f5db404
--- /dev/null
+++ b/src/soloud/src/filter/soloud_dcremovalfilter.cpp
@@ -0,0 +1,110 @@
+/*
+SoLoud audio engine
+Copyright (c) 2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_dcremovalfilter.h"
+
+namespace SoLoud
+{
+ DCRemovalFilterInstance::DCRemovalFilterInstance(DCRemovalFilter *aParent)
+ {
+ mParent = aParent;
+ mBuffer = 0;
+ mBufferLength = 0;
+ mTotals = 0;
+ mOffset = 0;
+ initParams(1);
+
+ }
+
+ void DCRemovalFilterInstance::filter(float *aBuffer, unsigned int aSamples, unsigned int aChannels, float aSamplerate, double aTime)
+ {
+ updateParams(aTime);
+
+ if (mBuffer == 0)
+ {
+ mBufferLength = (int)ceil(mParent->mLength * aSamplerate);
+ mBuffer = new float[mBufferLength * aChannels];
+ mTotals = new float[aChannels];
+ unsigned int i;
+ for (i = 0; i < aChannels; i++)
+ {
+ mTotals[i] = 0;
+ }
+ for (i = 0; i < mBufferLength * aChannels; i++)
+ {
+ mBuffer[i] = 0;
+ }
+ }
+
+ unsigned int i, j;
+ int prevofs = (mOffset + mBufferLength - 1) % mBufferLength;
+ for (i = 0; i < aSamples; i++)
+ {
+ for (j = 0; j < aChannels; j++)
+ {
+ int chofs = j * mBufferLength;
+ int bchofs = j * aSamples;
+
+ float n = aBuffer[i + bchofs];
+ mTotals[j] -= mBuffer[mOffset + chofs];
+ mTotals[j] += n;
+ mBuffer[mOffset + chofs] = n;
+
+ n -= mTotals[j] / mBufferLength;
+
+ aBuffer[i + bchofs] += (n - aBuffer[i + bchofs]) * mParam[0];
+ }
+ prevofs = mOffset;
+ mOffset = (mOffset + 1) % mBufferLength;
+ }
+ }
+
+ DCRemovalFilterInstance::~DCRemovalFilterInstance()
+ {
+ delete[] mBuffer;
+ delete[] mTotals;
+ }
+
+ DCRemovalFilter::DCRemovalFilter()
+ {
+ mLength = 0.1f;
+ }
+
+ result DCRemovalFilter::setParams(float aLength)
+ {
+ if (aLength <= 0)
+ return INVALID_PARAMETER;
+
+ mLength = aLength;
+
+ return 0;
+ }
+
+
+ FilterInstance *DCRemovalFilter::createInstance()
+ {
+ return new DCRemovalFilterInstance(this);
+ }
+}
diff --git a/src/soloud/src/filter/soloud_echofilter.cpp b/src/soloud/src/filter/soloud_echofilter.cpp
new file mode 100644
index 0000000..a9063f5
--- /dev/null
+++ b/src/soloud/src/filter/soloud_echofilter.cpp
@@ -0,0 +1,106 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "soloud.h"
+#include "soloud_echofilter.h"
+
+namespace SoLoud
+{
+ EchoFilterInstance::EchoFilterInstance(EchoFilter *aParent)
+ {
+ mParent = aParent;
+ mBuffer = 0;
+ mBufferLength = 0;
+ mOffset = 0;
+ initParams(1);
+
+ }
+
+ void EchoFilterInstance::filter(float *aBuffer, unsigned int aSamples, unsigned int aChannels, float aSamplerate, double aTime)
+ {
+ updateParams(aTime);
+
+ if (mBuffer == 0)
+ {
+ mBufferLength = (int)ceil(mParent->mDelay * aSamplerate);
+ mBuffer = new float[mBufferLength * aChannels];
+ unsigned int i;
+ for (i = 0; i < mBufferLength * aChannels; i++)
+ {
+ mBuffer[i] = 0;
+ }
+ }
+
+ float decay = mParent->mDecay;
+ unsigned int i, j;
+ int prevofs = (mOffset + mBufferLength - 1) % mBufferLength;
+ for (i = 0; i < aSamples; i++)
+ {
+ for (j = 0; j < aChannels; j++)
+ {
+ int chofs = j * mBufferLength;
+ int bchofs = j * aSamples;
+
+ mBuffer[mOffset + chofs] = mParent->mFilter * mBuffer[prevofs + chofs] + (1 - mParent->mFilter) * mBuffer[mOffset + chofs];
+
+ float n = aBuffer[i + bchofs] + mBuffer[mOffset + chofs] * decay;
+ mBuffer[mOffset + chofs] = n;
+
+ aBuffer[i + bchofs] += (n - aBuffer[i + bchofs]) * mParam[0];
+ }
+ prevofs = mOffset;
+ mOffset = (mOffset + 1) % mBufferLength;
+ }
+ }
+
+ EchoFilterInstance::~EchoFilterInstance()
+ {
+ delete[] mBuffer;
+ }
+
+ EchoFilter::EchoFilter()
+ {
+ mDelay = 0.3f;
+ mDecay = 0.7f;
+ mFilter = 0.0f;
+ }
+
+ result EchoFilter::setParams(float aDelay, float aDecay, float aFilter)
+ {
+ if (aDelay <= 0 || aDecay <= 0 || aFilter < 0 || aFilter >= 1.0f)
+ return INVALID_PARAMETER;
+
+ mDecay = aDecay;
+ mDelay = aDelay;
+ mFilter = aFilter;
+
+ return 0;
+ }
+
+
+ FilterInstance *EchoFilter::createInstance()
+ {
+ return new EchoFilterInstance(this);
+ }
+}
diff --git a/src/soloud/src/filter/soloud_fftfilter.cpp b/src/soloud/src/filter/soloud_fftfilter.cpp
new file mode 100644
index 0000000..6b7920d
--- /dev/null
+++ b/src/soloud/src/filter/soloud_fftfilter.cpp
@@ -0,0 +1,143 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2015 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include "soloud.h"
+#include "soloud_fftfilter.h"
+#include "soloud_fft.h"
+
+
+namespace SoLoud
+{
+ FFTFilterInstance::FFTFilterInstance()
+ {
+ mParent = 0;
+ mInputBuffer = 0;
+ mMixBuffer = 0;
+ mTemp = 0;
+ int i;
+ for (i = 0; i < MAX_CHANNELS; i++)
+ mOffset[i] = 0;
+ }
+
+ FFTFilterInstance::FFTFilterInstance(FFTFilter *aParent)
+ {
+ mParent = aParent;
+ mInputBuffer = 0;
+ mMixBuffer = 0;
+ mTemp = 0;
+ int i;
+ for (i = 0; i < MAX_CHANNELS; i++)
+ mOffset[i] = 0;
+ initParams(1);
+ }
+
+ void FFTFilterInstance::filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, double aTime, unsigned int aChannel, unsigned int aChannels)
+ {
+ if (aChannel == 0)
+ {
+ updateParams(aTime);
+ }
+
+ if (mInputBuffer == 0)
+ {
+ mInputBuffer = new float[512 * aChannels];
+ mMixBuffer = new float[512 * aChannels];
+ mTemp = new float[256];
+ memset(mInputBuffer, 0x2f, sizeof(float) * 512 * aChannels);
+ memset(mMixBuffer, 0, sizeof(float) * 512 * aChannels);
+ }
+
+ float * b = mTemp;
+
+ int i;
+ unsigned int ofs = 0;
+ unsigned int chofs = 512 * aChannel;
+ unsigned int bofs = mOffset[aChannel];
+
+ while (ofs < aSamples)
+ {
+ for (i = 0; i < 128; i++)
+ {
+ mInputBuffer[chofs + ((bofs + i + 128) & 511)] = aBuffer[ofs + i];
+ mMixBuffer[chofs + ((bofs + i + 128) & 511)] = 0;
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ b[i] = mInputBuffer[chofs + ((bofs + i) & 511)];
+ }
+ FFT::fft256(b);
+
+ // do magic
+ fftFilterChannel(b, 128, aSamplerate, aTime, aChannel, aChannels);
+
+ FFT::ifft256(b);
+
+ for (i = 0; i < 256; i++)
+ {
+ mMixBuffer[chofs + ((bofs + i) & 511)] += b[i] * (128 - abs(128 - i)) * (1.0f / 128.0f);
+ }
+
+ for (i = 0; i < 128; i++)
+ {
+ aBuffer[ofs + i] += (mMixBuffer[chofs + ((bofs + i) & 511)] - aBuffer[ofs + i]) * mParam[0];
+ }
+ ofs += 128;
+ bofs += 128;
+ }
+ mOffset[aChannel] = bofs;
+ }
+
+ void FFTFilterInstance::fftFilterChannel(float *aFFTBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels)
+ {
+ unsigned int i;
+ for (i = 4; i < aSamples; i++)
+ {
+ aFFTBuffer[(i - 4) * 2] = aFFTBuffer[i * 2];
+ aFFTBuffer[(i - 4) * 2 + 1] = aFFTBuffer[i * 2 + 1];
+ }
+ for (i = 0; i < 4; i++)
+ {
+ aFFTBuffer[aSamples - 4 * 2 + i * 2] = 0;
+ aFFTBuffer[aSamples - 4 * 2 + i * 2 + 1] = 0;
+ }
+ }
+
+ FFTFilterInstance::~FFTFilterInstance()
+ {
+ delete[] mTemp;
+ delete[] mInputBuffer;
+ delete[] mMixBuffer;
+ }
+
+ FFTFilter::FFTFilter()
+ {
+ }
+
+ FilterInstance *FFTFilter::createInstance()
+ {
+ return new FFTFilterInstance(this);
+ }
+}
diff --git a/src/soloud/src/filter/soloud_flangerfilter.cpp b/src/soloud/src/filter/soloud_flangerfilter.cpp
new file mode 100644
index 0000000..f1b0c12
--- /dev/null
+++ b/src/soloud/src/filter/soloud_flangerfilter.cpp
@@ -0,0 +1,109 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+#include
+#include "soloud.h"
+#include "soloud_flangerfilter.h"
+
+namespace SoLoud
+{
+ FlangerFilterInstance::FlangerFilterInstance(FlangerFilter *aParent)
+ {
+ mParent = aParent;
+ mBuffer = 0;
+ mBufferLength = 0;
+ mOffset = 0;
+ mIndex = 0;
+ initParams(3);
+ mParam[FlangerFilter::WET] = 1;
+ mParam[FlangerFilter::FREQ] = mParent->mFreq;
+ mParam[FlangerFilter::DELAY] = mParent->mDelay;
+ }
+
+ void FlangerFilterInstance::filter(float *aBuffer, unsigned int aSamples, unsigned int aChannels, float aSamplerate, double aTime)
+ {
+ updateParams(aTime);
+
+ if (mBufferLength < mParam[FlangerFilter::DELAY] * aSamplerate)
+ {
+ delete[] mBuffer;
+ mBufferLength = (int)ceil(mParam[FlangerFilter::DELAY] * aSamplerate);
+ mBuffer = new float[mBufferLength * aChannels];
+ if (mBuffer == NULL)
+ {
+ mBufferLength = 0;
+ return;
+ }
+ memset(mBuffer, 0, sizeof(float) * mBufferLength * aChannels);
+ }
+
+ unsigned int i, j;
+ int maxsamples = (int)ceil(mParam[FlangerFilter::DELAY] * aSamplerate);
+ double inc = mParam[FlangerFilter::FREQ] * M_PI * 2 / aSamplerate;
+ for (i = 0; i < aChannels; i++)
+ {
+ int mbofs = i * mBufferLength;
+ int abofs = i * aSamples;
+ for (j = 0; j < aSamples; j++, abofs++)
+ {
+ int delay = (int)floor(maxsamples * (1 + cos(mIndex))) / 2;
+ mIndex += inc;
+ mBuffer[mbofs + mOffset % mBufferLength] = aBuffer[abofs];
+ float n = 0.5f * (aBuffer[abofs] + mBuffer[mbofs + (mBufferLength - delay + mOffset) % mBufferLength]);
+ mOffset++;
+ aBuffer[abofs] += (n - aBuffer[abofs]) * mParam[FlangerFilter::WET];
+ }
+ mOffset -= aSamples;
+ }
+ mOffset += aSamples;
+ mOffset %= mBufferLength;
+ }
+
+ FlangerFilterInstance::~FlangerFilterInstance()
+ {
+ delete[] mBuffer;
+ }
+
+ FlangerFilter::FlangerFilter()
+ {
+ mDelay = 0.005f;
+ mFreq = 10;
+ }
+
+ result FlangerFilter::setParams(float aDelay, float aFreq)
+ {
+ if (aDelay <= 0 || aFreq <= 0)
+ return INVALID_PARAMETER;
+
+ mDelay = aDelay;
+ mFreq = aFreq;
+
+ return 0;
+ }
+
+
+ FilterInstance *FlangerFilter::createInstance()
+ {
+ return new FlangerFilterInstance(this);
+ }
+}
diff --git a/src/soloud/src/filter/soloud_lofifilter.cpp b/src/soloud/src/filter/soloud_lofifilter.cpp
new file mode 100644
index 0000000..26a9e9a
--- /dev/null
+++ b/src/soloud/src/filter/soloud_lofifilter.cpp
@@ -0,0 +1,95 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2014 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include "soloud.h"
+#include "soloud_lofifilter.h"
+
+namespace SoLoud
+{
+
+ LofiFilterInstance::LofiFilterInstance(LofiFilter *aParent)
+ {
+ mParent = aParent;
+ initParams(3);
+ mParam[SAMPLERATE] = aParent->mSampleRate;
+ mParam[BITDEPTH] = aParent->mBitdepth;
+ mChannelData[0].mSample = 0;
+ mChannelData[0].mSamplesToSkip = 0;
+ mChannelData[1].mSample = 0;
+ mChannelData[1].mSamplesToSkip = 0;
+ }
+
+ void LofiFilterInstance::filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, double aTime, unsigned int aChannel, unsigned int aChannels)
+ {
+ updateParams(aTime);
+
+ unsigned int i;
+ for (i = 0; i < aSamples; i++)
+ {
+ if (mChannelData[aChannel].mSamplesToSkip <= 0)
+ {
+ mChannelData[aChannel].mSamplesToSkip += (aSamplerate / mParam[SAMPLERATE]) - 1;
+ float q = (float)pow(2, mParam[BITDEPTH]);
+ mChannelData[aChannel].mSample = (float)floor(q*aBuffer[i])/q;
+ }
+ else
+ {
+ mChannelData[aChannel].mSamplesToSkip--;
+ }
+ aBuffer[i] += (mChannelData[aChannel].mSample - aBuffer[i]) * mParam[WET];
+ }
+
+ }
+
+ LofiFilterInstance::~LofiFilterInstance()
+ {
+ }
+
+ LofiFilter::LofiFilter()
+ {
+ setParams(4000, 3);
+ }
+
+ result LofiFilter::setParams(float aSampleRate, float aBitdepth)
+ {
+ if (aSampleRate <= 0 || aBitdepth <= 0)
+ return INVALID_PARAMETER;
+
+ mSampleRate = aSampleRate;
+ mBitdepth = aBitdepth;
+ return 0;
+ }
+
+ LofiFilter::~LofiFilter()
+ {
+ }
+
+
+ LofiFilterInstance *LofiFilter::createInstance()
+ {
+ return new LofiFilterInstance(this);
+ }
+}
diff --git a/src/soloud/src/filter/soloud_robotizefilter.cpp b/src/soloud/src/filter/soloud_robotizefilter.cpp
new file mode 100644
index 0000000..97c2e85
--- /dev/null
+++ b/src/soloud/src/filter/soloud_robotizefilter.cpp
@@ -0,0 +1,56 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include "soloud.h"
+#include "soloud_robotizefilter.h"
+
+
+namespace SoLoud
+{
+ RobotizeFilterInstance::RobotizeFilterInstance(RobotizeFilter *aParent)
+ {
+ mParent = aParent;
+ initParams(1);
+ mParam[WET] = 1.0;
+ }
+
+ void RobotizeFilterInstance::fftFilterChannel(float *aFFTBuffer, unsigned int aSamples, float aSamplerate, time aTime, unsigned int aChannel, unsigned int aChannels)
+ {
+ unsigned int i;
+ for (i = 0; i < aSamples; i++)
+ {
+ aFFTBuffer[i*2] = 0;
+ }
+ }
+
+ RobotizeFilter::RobotizeFilter()
+ {
+ }
+
+ FilterInstance *RobotizeFilter::createInstance()
+ {
+ return new RobotizeFilterInstance(this);
+ }
+}
diff --git a/src/soloud/src/filter/soloud_waveshaperfilter.cpp b/src/soloud/src/filter/soloud_waveshaperfilter.cpp
new file mode 100644
index 0000000..313073a
--- /dev/null
+++ b/src/soloud/src/filter/soloud_waveshaperfilter.cpp
@@ -0,0 +1,87 @@
+/*
+SoLoud audio engine
+Copyright (c) 2013-2018 Jari Komppa
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. 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
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include
+#include
+#include "soloud.h"
+#include "soloud_waveshaperfilter.h"
+
+namespace SoLoud
+{
+
+ WaveShaperFilterInstance::WaveShaperFilterInstance(WaveShaperFilter *aParent)
+ {
+ mParent = aParent;
+ initParams(2);
+ mParam[0] = mParent->mWet;
+ mParam[1] = mParent->mAmount;
+ }
+
+ void WaveShaperFilterInstance::filterChannel(float *aBuffer, unsigned int aSamples, float aSamplerate, double aTime, unsigned int aChannel, unsigned int aChannels)
+ {
+ updateParams(aTime);
+
+ unsigned int i;
+ float k = 0;
+ if (mParam[1] == 1)
+ k = 2 * mParam[1] / 0.01f;
+ else
+ k = 2 * mParam[1] / (1 - mParam[1]);
+
+ for (i = 0; i < aSamples; i++)
+ {
+ float dry = aBuffer[i];
+ float wet = (1 + k) * aBuffer[i] / (1 + k * (float)fabs(aBuffer[i]));
+ aBuffer[i] += (wet - dry) * mParam[0];
+ }
+ }
+
+ WaveShaperFilterInstance::~WaveShaperFilterInstance()
+ {
+ }
+
+ result WaveShaperFilter::setParams(float aAmount, float aWet)
+ {
+ if (aAmount < -1 || aAmount > 1 || aWet < 0 || aWet > 1)
+ return INVALID_PARAMETER;
+ mAmount = aAmount;
+ mWet = aWet;
+ return 0;
+ }
+
+ WaveShaperFilter::WaveShaperFilter()
+ {
+ mAmount = 0.0f;
+ mWet = 0.0f;
+ }
+
+ WaveShaperFilter::~WaveShaperFilter()
+ {
+ }
+
+ WaveShaperFilterInstance *WaveShaperFilter::createInstance()
+ {
+ return new WaveShaperFilterInstance(this);
+ }
+}
diff --git a/terminal.png b/terminal.png
new file mode 100644
index 0000000..3cffb5b
Binary files /dev/null and b/terminal.png differ
diff --git a/underwater.wav b/underwater.wav
new file mode 100644
index 0000000..727726e
Binary files /dev/null and b/underwater.wav differ