Vendored dependencies (no more submodules)

This commit is contained in:
John Alanbrook 2024-03-13 17:07:34 -05:00
parent 962fed0022
commit 52fe4b56fa
710 changed files with 451219 additions and 29 deletions

18
.gitmodules vendored
View file

@ -1,18 +0,0 @@
[submodule "source/engine/thirdparty/sokol"]
path = source/engine/thirdparty/sokol
url = https://github.com/floooh/sokol.git
[submodule "source/engine/thirdparty/stb"]
path = source/engine/thirdparty/stb
url = https://github.com/nothings/stb.git
[submodule "source/engine/thirdparty/cgltf"]
path = source/engine/thirdparty/cgltf
url = https://github.com/jkuhlmann/cgltf.git
[submodule "quickjs"]
path = quickjs
url = https://github.com/bellard/quickjs.git
[submodule "source/engine/thirdparty/TinySoundFont"]
path = source/engine/thirdparty/TinySoundFont
url = https://github.com/schellingb/TinySoundFont.git
[submodule "source/engine/thirdparty/dr_libs"]
path = source/engine/thirdparty/dr_libs
url = https://github.com/mackron/dr_libs.git

View file

@ -159,7 +159,7 @@ endif
OBJDIR = $(BIN)/obj OBJDIR = $(BIN)/obj
# All other sources # All other sources
OBJS != find source/engine -type f -name '*.c' | grep -vE 'test|tool|example|fuzz|main' OBJS != find source/engine -type f -name '*.c' | grep -vE 'test|tool|example|fuzz|main' | grep -vE 'quickjs'
CPPOBJS != find source/engine -type f -name '*.cpp' | grep -vE 'test|tool|example|fuzz|main' CPPOBJS != find source/engine -type f -name '*.cpp' | grep -vE 'test|tool|example|fuzz|main'
OBJS += $(CPPOBJS) OBJS += $(CPPOBJS)
OBJS += $(shell find source/engine -type f -name '*.m') OBJS += $(shell find source/engine -type f -name '*.m')
@ -256,11 +256,12 @@ input.md: $(INPUTMD)
@echo Printing api for $* @echo Printing api for $*
@./primum -d $* > $@ @./primum -d $* > $@
QUICKJS := source/engine/thirdparty/quickjs
$(BIN)/libquickjs.a: $(BIN)/libquickjs.a:
make -C quickjs clean make -C $(QUICKJS) clean
make -C quickjs SYSRT=$(SYSRT) TTARGET=$(TTARGET) ARCH=$(ARCH) DBG=$(DBG) OPT=$(OPT) AR=$(AR) OS=$(OS) libquickjs.a HOST_CC=$(CC) LEAK=$(LEAK) make -C $(QUICKJS) SYSRT=$(SYSRT) TTARGET=$(TTARGET) ARCH=$(ARCH) DBG=$(DBG) OPT=$(OPT) AR=$(AR) OS=$(OS) libquickjs.a HOST_CC=$(CC) LEAK=$(LEAK)
@mkdir -p $(BIN) @mkdir -p $(BIN)
cp -rf quickjs/libquickjs.* $(BIN) cp -rf $(QUICKJS)/libquickjs.* $(BIN)
$(OBJDIR)/%.o: %.c source/engine/core.cdb.h $(SHADERS) $(OBJDIR)/%.o: %.c source/engine/core.cdb.h $(SHADERS)
@mkdir -p $(@D) @mkdir -p $(@D)
@ -332,7 +333,7 @@ clean:
rm -rf bin dist rm -rf bin dist
rm -f source/shaders/*.h core.cdb jso cdb packer TAGS source/engine/core.cdb.h tools/libcdb.a $(CDB)/libcdb.a rm -f source/shaders/*.h core.cdb jso cdb packer TAGS source/engine/core.cdb.h tools/libcdb.a $(CDB)/libcdb.a
rm -f $(CDB)/*.o rm -f $(CDB)/*.o
@make -C quickjs clean @make -C $(QUICKJS) clean
docs: doc/prosperon.org docs: doc/prosperon.org
make -C doc make -C doc

@ -1 +0,0 @@
Subproject commit 6a89d7c27099be84e5312a7ec73205d6a7abe1b4

@ -1 +0,0 @@
Subproject commit 92a8f0e9fe3c98358be7d8564db21fc4b1142d04

View file

@ -0,0 +1,19 @@
Copyright (C) 2017-2023 Bernhard Schelling (Based on SFZero, Copyright (C) 2012 Steve Folta, https://github.com/stevefolta/SFZero)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,37 @@
# TinySoundFont
SoundFont2 synthesizer library in a single C/C++ file
## Overview
TinySoundFont is a software synthesizer using SoundFont2 sound bank files.
The library is a single C header file so it is extremely simple to integrate in your C/C++ projects.
```c++
#define TSF_IMPLEMENTATION
#include "tsf.h"
...
tsf* TinySoundFont = tsf_load_filename("soundfont.sf2");
tsf_set_output(TinySoundFont, TSF_MONO, 44100, 0); //sample rate
tsf_note_on(TinySoundFont, 0, 60, 1.0f); //preset 0, middle C
short HalfSecond[22050]; //synthesize 0.5 seconds
tsf_render_short(TinySoundFont, HalfSecond, 22050, 0);
```
The library code is based on [SFZero by Steve Folta](https://github.com/stevefolta/SFZero).
## Documentation
The API documentation can be found on [top of the library source code](https://github.com/schellingb/TinySoundFont/blob/master/tsf.h).
There are also [examples available](https://github.com/schellingb/TinySoundFont/tree/master/examples) which come with a sample SoundFont file and build and play sound on Win32, Win64, Linux and MacOSX with no further dependencies.
## Dependencies
C standard libraries for fopen, math and malloc (can be removed by providing custom functions with #defines).
## License
TinySoundFont is available under the [MIT license](https://choosealicense.com/licenses/mit/).

View file

@ -0,0 +1,15 @@
*.ncb
*.opt
*.plg
*.aps
*.ipch
*.suo
*.user
*.sdf
*.opensdf
*.dsw
*-i686
*-x86_64
Debug/
Release/
.vs/

View file

@ -0,0 +1,7 @@
echo Building \'example1-linux-`uname -m`\' ...
clang -std=c99 -Wall example1.c minisdl_audio.c -lm -ldl -lpthread -o example1-linux-`uname -m`
echo Building \'example2-linux-`uname -m`\' ...
clang -std=c99 -Wall example2.c minisdl_audio.c -lm -ldl -lpthread -o example2-linux-`uname -m`
echo Building \'example3-linux-`uname -m`\' ...
clang -std=c99 -Wall example3.c minisdl_audio.c -lm -ldl -lpthread -o example3-linux-`uname -m`
echo Done!

View file

@ -0,0 +1,7 @@
echo Building \'example1-linux-`uname -m`\' ...
gcc -Wall example1.c minisdl_audio.c -lm -ldl -lpthread -o example1-linux-`uname -m`
echo Building \'example2-linux-`uname -m`\' ...
gcc -Wall example2.c minisdl_audio.c -lm -ldl -lpthread -o example2-linux-`uname -m`
echo Building \'example3-linux-`uname -m`\' ...
gcc -Wall example3.c minisdl_audio.c -lm -ldl -lpthread -o example3-linux-`uname -m`
echo Done!

View file

@ -0,0 +1,7 @@
echo Building \'example1-osx-`uname -m`\' ...
clang -std=c99 -Wall example1.c minisdl_audio.c -lm -ldl -lpthread -framework CoreServices -framework CoreAudio -framework AudioUnit -o example1-osx-`uname -m`
echo Building \'example2-osx-`uname -m`\' ...
clang -std=c99 -Wall example2.c minisdl_audio.c -lm -ldl -lpthread -framework CoreServices -framework CoreAudio -framework AudioUnit -o example2-osx-`uname -m`
echo Building \'example3-osx-`uname -m`\' ...
clang -std=c99 -Wall example3.c minisdl_audio.c -lm -ldl -lpthread -framework CoreServices -framework CoreAudio -framework AudioUnit -o example3-osx-`uname -m`
echo Done!

View file

@ -0,0 +1,86 @@
#define TSF_IMPLEMENTATION
#include "../tsf.h"
#include "minisdl_audio.h"
//This is a minimal SoundFont with a single loopin saw-wave sample/instrument/preset (484 bytes)
const static unsigned char MinimalSoundFont[] =
{
#define TEN0 0,0,0,0,0,0,0,0,0,0
'R','I','F','F',220,1,0,0,'s','f','b','k',
'L','I','S','T',88,1,0,0,'p','d','t','a',
'p','h','d','r',76,TEN0,TEN0,TEN0,TEN0,0,0,0,0,TEN0,0,0,0,0,0,0,0,255,0,255,0,1,TEN0,0,0,0,
'p','b','a','g',8,0,0,0,0,0,0,0,1,0,0,0,'p','m','o','d',10,TEN0,0,0,0,'p','g','e','n',8,0,0,0,41,0,0,0,0,0,0,0,
'i','n','s','t',44,TEN0,TEN0,0,0,0,0,0,0,0,0,TEN0,0,0,0,0,0,0,0,1,0,
'i','b','a','g',8,0,0,0,0,0,0,0,2,0,0,0,'i','m','o','d',10,TEN0,0,0,0,
'i','g','e','n',12,0,0,0,54,0,1,0,53,0,0,0,0,0,0,0,
's','h','d','r',92,TEN0,TEN0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,49,0,0,0,34,86,0,0,60,0,0,0,1,TEN0,TEN0,TEN0,TEN0,0,0,0,0,0,0,0,
'L','I','S','T',112,0,0,0,'s','d','t','a','s','m','p','l',100,0,0,0,86,0,119,3,31,7,147,10,43,14,169,17,58,21,189,24,73,28,204,31,73,35,249,38,46,42,71,46,250,48,150,53,242,55,126,60,151,63,108,66,126,72,207,
70,86,83,100,72,74,100,163,39,241,163,59,175,59,179,9,179,134,187,6,186,2,194,5,194,15,200,6,202,96,206,159,209,35,213,213,216,45,220,221,223,76,227,221,230,91,234,242,237,105,241,8,245,118,248,32,252
};
// Holds the global instance pointer
static tsf* g_TinySoundFont;
// Callback function called by the audio thread
static void AudioCallback(void* data, Uint8 *stream, int len)
{
// Note we don't do any thread concurrency control here because in this
// example all notes are started before the audio playback begins.
// If you do play notes while the audio thread renders output you
// will need a mutex of some sort.
int SampleCount = (len / (2 * sizeof(short))); //2 output channels
tsf_render_short(g_TinySoundFont, (short*)stream, SampleCount, 0);
}
int main(int argc, char *argv[])
{
// Define the desired audio output format we request
SDL_AudioSpec OutputAudioSpec;
OutputAudioSpec.freq = 44100;
OutputAudioSpec.format = AUDIO_S16;
OutputAudioSpec.channels = 2;
OutputAudioSpec.samples = 4096;
OutputAudioSpec.callback = AudioCallback;
// Initialize the audio system
if (SDL_AudioInit(NULL) < 0)
{
fprintf(stderr, "Could not initialize audio hardware or driver\n");
return 1;
}
// Load the SoundFont from the memory block
g_TinySoundFont = tsf_load_memory(MinimalSoundFont, sizeof(MinimalSoundFont));
if (!g_TinySoundFont)
{
fprintf(stderr, "Could not load soundfont\n");
return 1;
}
// Set the rendering output mode to 44.1khz and -10 decibel gain
tsf_set_output(g_TinySoundFont, TSF_STEREO_INTERLEAVED, OutputAudioSpec.freq, -10);
// Start two notes before starting the audio playback
tsf_note_on(g_TinySoundFont, 0, 48, 1.0f); //C2
tsf_note_on(g_TinySoundFont, 0, 52, 1.0f); //E2
// Request the desired audio output format
if (SDL_OpenAudio(&OutputAudioSpec, NULL) < 0)
{
fprintf(stderr, "Could not open the audio hardware or the desired audio output format\n");
return 1;
}
// Start the actual audio playback here
// The audio thread will begin to call our AudioCallback function
SDL_PauseAudio(0);
// Let the audio callback play some sound for 3 seconds
SDL_Delay(3000);
// We could call tsf_close(g_TinySoundFont) and SDL_DestroyMutex(g_Mutex)
// here to free the memory and resources but we just let the OS clean up
// because the process ends here.
return 0;
}

View file

@ -0,0 +1,77 @@
# Microsoft Developer Studio Project File - Name="example1" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=example1 - Win32 Debug
!MESSAGE "example1 - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "example1 - Win32 Debug" (based on "Win32 (x86) Console Application")
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "example1 - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x807 /d "NDEBUG"
# ADD RSC /l 0x807 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "example1 - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0x807 /d "_DEBUG"
# ADD RSC /l 0x807 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "example1 - Win32 Release"
# Name "example1 - Win32 Debug"
# Begin Source File
SOURCE=example1.c
# End Source File
# Begin Source File
SOURCE=minisdl_audio.c
# End Source File
# End Target
# End Project

View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{A47C788B-1BDA-4057-87A9-FC35ED711B44}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>example1</RootNamespace>
<ProjectName>example1</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '11.0' Or '$(PlatformToolsetVersion)' == '110' Or '$(MSBuildToolsVersion)' == '4.0'">v110</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '12.0' Or '$(PlatformToolsetVersion)' == '120' Or '$(MSBuildToolsVersion)' == '12.0'">v120</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0' Or '$(PlatformToolsetVersion)' == '140' Or '$(MSBuildToolsVersion)' == '14.0'">v140</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0' Or '$(PlatformToolsetVersion)' == '141' Or '$(MSBuildToolsVersion)' == '15.0'">v141</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0' Or '$(PlatformToolsetVersion)' == '142' Or '$(MSBuildToolsVersion)' == '16.0'">v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<OutDir>$(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\</OutDir>
<IntDir>$(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<IntrinsicFunctions>true</IntrinsicFunctions>
<ExceptionHandling>false</ExceptionHandling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateMapFile>true</GenerateMapFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions Condition="'$(VisualStudioVersion)' &gt;= '12.0' Or '$(PlatformToolsetVersion)' &gt;= '120' Or '$(MSBuildToolsVersion)' &gt;= '12.0'">/Gw %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemGroup>
<ClCompile Include="minisdl_audio.c" />
<ClCompile Include="example1.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="minisdl_audio.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\tsf.h" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,81 @@
#include "minisdl_audio.h"
#define TSF_IMPLEMENTATION
#include "../tsf.h"
// Holds the global instance pointer
static tsf* g_TinySoundFont;
// A Mutex so we don't call note_on/note_off while rendering audio samples
static SDL_mutex* g_Mutex;
static void AudioCallback(void* data, Uint8 *stream, int len)
{
// Render the audio samples in float format
int SampleCount = (len / (2 * sizeof(float))); //2 output channels
SDL_LockMutex(g_Mutex); //get exclusive lock
tsf_render_float(g_TinySoundFont, (float*)stream, SampleCount, 0);
SDL_UnlockMutex(g_Mutex);
}
int main(int argc, char *argv[])
{
int i, Notes[7] = { 48, 50, 52, 53, 55, 57, 59 };
// Define the desired audio output format we request
SDL_AudioSpec OutputAudioSpec;
OutputAudioSpec.freq = 44100;
OutputAudioSpec.format = AUDIO_F32;
OutputAudioSpec.channels = 2;
OutputAudioSpec.samples = 4096;
OutputAudioSpec.callback = AudioCallback;
// Initialize the audio system
if (SDL_AudioInit(TSF_NULL) < 0)
{
fprintf(stderr, "Could not initialize audio hardware or driver\n");
return 1;
}
// Load the SoundFont from a file
g_TinySoundFont = tsf_load_filename("florestan-subset.sf2");
if (!g_TinySoundFont)
{
fprintf(stderr, "Could not load SoundFont\n");
return 1;
}
// Set the SoundFont rendering output mode
tsf_set_output(g_TinySoundFont, TSF_STEREO_INTERLEAVED, OutputAudioSpec.freq, 0);
// Create the mutex
g_Mutex = SDL_CreateMutex();
// Request the desired audio output format
if (SDL_OpenAudio(&OutputAudioSpec, TSF_NULL) < 0)
{
fprintf(stderr, "Could not open the audio hardware or the desired audio output format\n");
return 1;
}
// Start the actual audio playback here
// The audio thread will begin to call our AudioCallback function
SDL_PauseAudio(0);
// Loop through all the presets in the loaded SoundFont
for (i = 0; i < tsf_get_presetcount(g_TinySoundFont); i++)
{
//Get exclusive mutex lock, end the previous note and play a new note
printf("Play note %d with preset #%d '%s'\n", Notes[i % 7], i, tsf_get_presetname(g_TinySoundFont, i));
SDL_LockMutex(g_Mutex);
tsf_note_off(g_TinySoundFont, i - 1, Notes[(i - 1) % 7]);
tsf_note_on(g_TinySoundFont, i, Notes[i % 7], 1.0f);
SDL_UnlockMutex(g_Mutex);
SDL_Delay(1000);
}
// We could call tsf_close(g_TinySoundFont) and SDL_DestroyMutex(g_Mutex)
// here to free the memory and resources but we just let the OS clean up
// because the process ends here.
return 0;
}

View file

@ -0,0 +1,77 @@
# Microsoft Developer Studio Project File - Name="example2" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=example2 - Win32 Debug
!MESSAGE "example2 - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "example2 - Win32 Debug" (based on "Win32 (x86) Console Application")
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "example2 - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x807 /d "NDEBUG"
# ADD RSC /l 0x807 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "example2 - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0x807 /d "_DEBUG"
# ADD RSC /l 0x807 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "example2 - Win32 Release"
# Name "example2 - Win32 Debug"
# Begin Source File
SOURCE=example2.c
# End Source File
# Begin Source File
SOURCE=minisdl_audio.c
# End Source File
# End Target
# End Project

View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{78C3A807-00CB-410B-8F75-04910BB3975F}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>example2</RootNamespace>
<ProjectName>example2</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '11.0' Or '$(PlatformToolsetVersion)' == '110' Or '$(MSBuildToolsVersion)' == '4.0'">v110</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '12.0' Or '$(PlatformToolsetVersion)' == '120' Or '$(MSBuildToolsVersion)' == '12.0'">v120</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0' Or '$(PlatformToolsetVersion)' == '140' Or '$(MSBuildToolsVersion)' == '14.0'">v140</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0' Or '$(PlatformToolsetVersion)' == '141' Or '$(MSBuildToolsVersion)' == '15.0'">v141</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0' Or '$(PlatformToolsetVersion)' == '142' Or '$(MSBuildToolsVersion)' == '16.0'">v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<OutDir>$(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\</OutDir>
<IntDir>$(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<IntrinsicFunctions>true</IntrinsicFunctions>
<ExceptionHandling>false</ExceptionHandling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateMapFile>true</GenerateMapFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions Condition="'$(VisualStudioVersion)' &gt;= '12.0' Or '$(PlatformToolsetVersion)' &gt;= '120' Or '$(MSBuildToolsVersion)' &gt;= '12.0'">/Gw %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemGroup>
<ClCompile Include="minisdl_audio.c" />
<ClCompile Include="example2.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="minisdl_audio.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\tsf.h" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,125 @@
#include "minisdl_audio.h"
#define TSF_IMPLEMENTATION
#include "../tsf.h"
#define TML_IMPLEMENTATION
#include "../tml.h"
// Holds the global instance pointer
static tsf* g_TinySoundFont;
// Holds global MIDI playback state
static double g_Msec; //current playback time
static tml_message* g_MidiMessage; //next message to be played
// Callback function called by the audio thread
static void AudioCallback(void* data, Uint8 *stream, int len)
{
//Number of samples to process
int SampleBlock, SampleCount = (len / (2 * sizeof(float))); //2 output channels
for (SampleBlock = TSF_RENDER_EFFECTSAMPLEBLOCK; SampleCount; SampleCount -= SampleBlock, stream += (SampleBlock * (2 * sizeof(float))))
{
//We progress the MIDI playback and then process TSF_RENDER_EFFECTSAMPLEBLOCK samples at once
if (SampleBlock > SampleCount) SampleBlock = SampleCount;
//Loop through all MIDI messages which need to be played up until the current playback time
for (g_Msec += SampleBlock * (1000.0 / 44100.0); g_MidiMessage && g_Msec >= g_MidiMessage->time; g_MidiMessage = g_MidiMessage->next)
{
switch (g_MidiMessage->type)
{
case TML_PROGRAM_CHANGE: //channel program (preset) change (special handling for 10th MIDI channel with drums)
tsf_channel_set_presetnumber(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->program, (g_MidiMessage->channel == 9));
break;
case TML_NOTE_ON: //play a note
tsf_channel_note_on(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->key, g_MidiMessage->velocity / 127.0f);
break;
case TML_NOTE_OFF: //stop a note
tsf_channel_note_off(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->key);
break;
case TML_PITCH_BEND: //pitch wheel modification
tsf_channel_set_pitchwheel(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->pitch_bend);
break;
case TML_CONTROL_CHANGE: //MIDI controller messages
tsf_channel_midi_control(g_TinySoundFont, g_MidiMessage->channel, g_MidiMessage->control, g_MidiMessage->control_value);
break;
}
}
// Render the block of audio samples in float format
tsf_render_float(g_TinySoundFont, (float*)stream, SampleBlock, 0);
}
}
int main(int argc, char *argv[])
{
// This implements a small program that you can launch without
// parameters for a default file & soundfont, or with these arguments:
//
// ./example3-... <yourfile>.mid <yoursoundfont>.sf2
tml_message* TinyMidiLoader = NULL;
// Define the desired audio output format we request
SDL_AudioSpec OutputAudioSpec;
OutputAudioSpec.freq = 44100;
OutputAudioSpec.format = AUDIO_F32;
OutputAudioSpec.channels = 2;
OutputAudioSpec.samples = 4096;
OutputAudioSpec.callback = AudioCallback;
// Initialize the audio system
if (SDL_AudioInit(TSF_NULL) < 0)
{
fprintf(stderr, "Could not initialize audio hardware or driver\n");
return 1;
}
//Venture (Original WIP) by Ximon
//https://musescore.com/user/2391686/scores/841451
//License: Creative Commons copyright waiver (CC0)
TinyMidiLoader = tml_load_filename((argc >= 2 ? argv[1] : "venture.mid"));
if (!TinyMidiLoader)
{
fprintf(stderr, "Could not load MIDI file\n");
return 1;
}
//Set up the global MidiMessage pointer to the first MIDI message
g_MidiMessage = TinyMidiLoader;
// Load the SoundFont from a file
g_TinySoundFont = tsf_load_filename(
(argc >= 3 ? argv[2] : "florestan-subset.sf2")
);
if (!g_TinySoundFont)
{
fprintf(stderr, "Could not load SoundFont\n");
return 1;
}
//Initialize preset on special 10th MIDI channel to use percussion sound bank (128) if available
tsf_channel_set_bank_preset(g_TinySoundFont, 9, 128, 0);
// Set the SoundFont rendering output mode
tsf_set_output(g_TinySoundFont, TSF_STEREO_INTERLEAVED, OutputAudioSpec.freq, 0.0f);
// Request the desired audio output format
if (SDL_OpenAudio(&OutputAudioSpec, TSF_NULL) < 0)
{
fprintf(stderr, "Could not open the audio hardware or the desired audio output format\n");
return 1;
}
// Start the actual audio playback here
// The audio thread will begin to call our AudioCallback function
SDL_PauseAudio(0);
//Wait until the entire MIDI file has been played back (until the end of the linked message list is reached)
while (g_MidiMessage != NULL) SDL_Delay(100);
// We could call tsf_close(g_TinySoundFont) and tml_free(TinyMidiLoader)
// here to free the memory and resources but we just let the OS clean up
// because the process ends here.
return 0;
}

View file

@ -0,0 +1,77 @@
# Microsoft Developer Studio Project File - Name="example3" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=example3 - Win32 Debug
!MESSAGE "example3 - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "example3 - Win32 Debug" (based on "Win32 (x86) Console Application")
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "example3 - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x807 /d "NDEBUG"
# ADD RSC /l 0x807 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
!ELSEIF "$(CFG)" == "example3 - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0x807 /d "_DEBUG"
# ADD RSC /l 0x807 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "example3 - Win32 Release"
# Name "example3 - Win32 Debug"
# Begin Source File
SOURCE=example3.c
# End Source File
# Begin Source File
SOURCE=minisdl_audio.c
# End Source File
# End Target
# End Project

View file

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{78C3A807-00CB-410B-8F75-04910BB39760}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>example3</RootNamespace>
<ProjectName>example3</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '11.0' Or '$(PlatformToolsetVersion)' == '110' Or '$(MSBuildToolsVersion)' == '4.0'">v110</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '12.0' Or '$(PlatformToolsetVersion)' == '120' Or '$(MSBuildToolsVersion)' == '12.0'">v120</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0' Or '$(PlatformToolsetVersion)' == '140' Or '$(MSBuildToolsVersion)' == '14.0'">v140</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0' Or '$(PlatformToolsetVersion)' == '141' Or '$(MSBuildToolsVersion)' == '15.0'">v141</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0' Or '$(PlatformToolsetVersion)' == '142' Or '$(MSBuildToolsVersion)' == '16.0'">v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<OutDir>$(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\</OutDir>
<IntDir>$(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<IntrinsicFunctions>true</IntrinsicFunctions>
<ExceptionHandling>false</ExceptionHandling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateMapFile>true</GenerateMapFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions Condition="'$(VisualStudioVersion)' &gt;= '12.0' Or '$(PlatformToolsetVersion)' &gt;= '120' Or '$(MSBuildToolsVersion)' &gt;= '12.0'">/Gw %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemGroup>
<ClCompile Include="minisdl_audio.c" />
<ClCompile Include="example3.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\tml.h" />
<ClInclude Include="minisdl_audio.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\tsf.h" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,48 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example1", "example1.vcxproj", "{A47C788B-1BDA-4057-87A9-FC35ED711B44}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example2", "example2.vcxproj", "{78C3A807-00CB-410B-8F75-04910BB3975F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example3", "example3.vcxproj", "{78C3A807-00CB-410B-8F75-04910BB39760}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A47C788B-1BDA-4057-87A9-FC35ED711B44}.Debug|Win32.ActiveCfg = Debug|Win32
{A47C788B-1BDA-4057-87A9-FC35ED711B44}.Debug|Win32.Build.0 = Debug|Win32
{A47C788B-1BDA-4057-87A9-FC35ED711B44}.Debug|x64.ActiveCfg = Debug|x64
{A47C788B-1BDA-4057-87A9-FC35ED711B44}.Debug|x64.Build.0 = Debug|x64
{A47C788B-1BDA-4057-87A9-FC35ED711B44}.Release|Win32.ActiveCfg = Release|Win32
{A47C788B-1BDA-4057-87A9-FC35ED711B44}.Release|Win32.Build.0 = Release|Win32
{A47C788B-1BDA-4057-87A9-FC35ED711B44}.Release|x64.ActiveCfg = Release|x64
{A47C788B-1BDA-4057-87A9-FC35ED711B44}.Release|x64.Build.0 = Release|x64
{78C3A807-00CB-410B-8F75-04910BB3975F}.Debug|Win32.ActiveCfg = Debug|Win32
{78C3A807-00CB-410B-8F75-04910BB3975F}.Debug|Win32.Build.0 = Debug|Win32
{78C3A807-00CB-410B-8F75-04910BB3975F}.Debug|x64.ActiveCfg = Debug|x64
{78C3A807-00CB-410B-8F75-04910BB3975F}.Debug|x64.Build.0 = Debug|x64
{78C3A807-00CB-410B-8F75-04910BB3975F}.Release|Win32.ActiveCfg = Release|Win32
{78C3A807-00CB-410B-8F75-04910BB3975F}.Release|Win32.Build.0 = Release|Win32
{78C3A807-00CB-410B-8F75-04910BB3975F}.Release|x64.ActiveCfg = Release|x64
{78C3A807-00CB-410B-8F75-04910BB3975F}.Release|x64.Build.0 = Release|x64
{78C3A807-00CB-410B-8F75-04910BB39760}.Debug|Win32.ActiveCfg = Debug|Win32
{78C3A807-00CB-410B-8F75-04910BB39760}.Debug|Win32.Build.0 = Debug|Win32
{78C3A807-00CB-410B-8F75-04910BB39760}.Debug|x64.ActiveCfg = Debug|x64
{78C3A807-00CB-410B-8F75-04910BB39760}.Debug|x64.Build.0 = Debug|x64
{78C3A807-00CB-410B-8F75-04910BB39760}.Release|Win32.ActiveCfg = Release|Win32
{78C3A807-00CB-410B-8F75-04910BB39760}.Release|Win32.Build.0 = Release|Win32
{78C3A807-00CB-410B-8F75-04910BB39760}.Release|x64.ActiveCfg = Release|x64
{78C3A807-00CB-410B-8F75-04910BB39760}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = TRUE
EndGlobalSection
EndGlobal

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -0,0 +1,15 @@
*.ncb
*.opt
*.plg
*.aps
*.ipch
*.suo
*.user
*.sdf
*.opensdf
*.dsw
*-i686
*-x86_64
Debug
Release
.vs

View file

@ -0,0 +1,2 @@
all:
gcc main.c -o sfotool

View file

@ -0,0 +1,29 @@
# SFOTool for TinySoundFont
A tool to create heavily compressed .SFO files from .SF2 SoundFont files.
## Purpose
SFO files are just regular SoundFont v2 files with the entire block of raw PCM samples replaced
with a single Ogg Vorbis compressed stream. Unlike .sf3 files, which can have every separate font
sample compressed individually, this will compress the entire sound data as if it were a single
sample. This results in much higher compression than processing samples individually but also
higher loss of quality.
## Usage Help
```sh
sfotool <SF2/SFO>: Show type of sample stream contained (PCM or OGG)
sfotool <SF2> <WAV>: Dump PCM sample stream to .WAV file
sfotool <SFO> <OGG>: Dump OGG sample stream to .OGG file
sfotool <SF2/SFO> <WAV> <SF2>: Write new .SF2 soundfont file using PCM sample stream from .WAV file
sfotool <SF2/SFO> <OGG> <SFO>: Write new .SFO soundfont file using OGG sample stream from .OGG file
```
## Making a .SFO file from a .SF2 file
1. Dump the PCM data of a .SF2 file to a .WAV file
`sfotool <SF2> <WAV>`
2. Compress the .WAV file to .OGG (i.e. with [Audacity](https://www.audacityteam.org/download/legacy-windows/))
Make sure to choose the desired compression quality level
3. Build the .SFO file from the .SF2 file and the new .OGG
`sfotool <SF2> <OGG> <SFO>`
# License
SFOTool is available under the [Unlicense](http://unlicense.org/) (public domain).

View file

@ -0,0 +1,200 @@
//--------------------------------------------//
// SFOTool //
// License: Public Domain (www.unlicense.org) //
//--------------------------------------------//
#include <stdio.h>
#include <string.h>
typedef char sfo_fourcc[4];
#define SFO_FourCCEquals(a, b) (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3])
struct sfo_riffchunk { sfo_fourcc id; unsigned int size; };
struct sfo_wavheader
{
char RIFF[4]; unsigned int ChunkSize; char WAVE[4], fmt[4]; unsigned int Subchunk1Size;
unsigned short AudioFormat,NumOfChan; unsigned int SamplesPerSec, bytesPerSec;
unsigned short blockAlign, bitsPerSample; char Subchunk2ID[4]; unsigned int Subchunk2Size;
};
static void sfo_copy(FILE* src, FILE* trg, unsigned int size)
{
unsigned int block;
unsigned char buf[512];
for (; size; size -= block)
{
block = (size > sizeof(buf) ? sizeof(buf) : size);
fread(buf, 1, block, src);
fwrite(buf, 1, block, trg);
}
}
static int sfo_riffchunk_read(struct sfo_riffchunk* parent, struct sfo_riffchunk* chunk, FILE* f)
{
int is_riff, is_list;
if (parent && sizeof(sfo_fourcc) + sizeof(unsigned int) > parent->size) return 0;
if (!fread(&chunk->id, sizeof(sfo_fourcc), 1, f) || *chunk->id <= ' ' || *chunk->id >= 'z') return 0;
if (!fread(&chunk->size, sizeof(unsigned int), 1, f)) return 0;
if (parent && sizeof(sfo_fourcc) + sizeof(unsigned int) + chunk->size > parent->size) return 0;
if (parent) parent->size -= sizeof(sfo_fourcc) + sizeof(unsigned int) + chunk->size;
is_riff = SFO_FourCCEquals(chunk->id, "RIFF"), is_list = SFO_FourCCEquals(chunk->id, "LIST");
if (is_riff && parent) return 0; /* not allowed */
if (!is_riff && !is_list) return 1; /* custom type without sub type */
if (!fread(&chunk->id, sizeof(sfo_fourcc), 1, f) || *chunk->id <= ' ' || *chunk->id >= 'z') return 0;
chunk->size -= sizeof(sfo_fourcc);
return 1;
}
int main(int argc, const char** argv)
{
const char* arg_sf_in = (argc > 1 ? argv[1] : NULL);
const char* arg_smpl = (argc > 2 ? argv[2] : NULL);
const char* arg_sf_out = (argc > 3 ? argv[3] : NULL);
char ext_sf_in = (arg_sf_in ? arg_sf_in [strlen(arg_sf_in)-1] | 0x20 : '\0');
char ext_smpl = (arg_smpl ? arg_smpl [strlen(arg_smpl)-1] | 0x20 : '\0');
char ext_sf_out = (arg_sf_out ? arg_sf_out[strlen(arg_sf_out)-1] | 0x20 : '\0');
struct sfo_riffchunk chunkHead, chunkList, chunk;
FILE* f_sf_in = NULL, *f_smpl = NULL, *f_sf_out = NULL;
if (argc < 2 || argc > 4)
{
print_usage:
fprintf(stderr, "Usage Help:\n");
fprintf(stderr, "%s <SF2/SFO>: Show type of sample stream contained (PCM or OGG)\n", argv[0]);
fprintf(stderr, "%s <SF2> <WAV>: Dump PCM sample stream to .WAV file\n", argv[0]);
fprintf(stderr, "%s <SFO> <OGG>: Dump OGG sample stream to .OGG file\n", argv[0]);
fprintf(stderr, "%s <SF2/SFO> <WAV> <SF2>: Write new .SF2 soundfont file using PCM sample stream from .WAV file\n", argv[0]);
fprintf(stderr, "%s <SF2/SFO> <OGG> <SFO>: Write new .SFO soundfont file using OGG sample stream from .OGG file\n", argv[0]);
if (f_sf_in) fclose(f_sf_in);
if (f_smpl) fclose(f_smpl);
if (f_sf_out) fclose(f_sf_out);
return 1;
}
f_sf_in = fopen(arg_sf_in, "rb");
if (!f_sf_in) { fprintf(stderr, "Error: Passed input file '%s' does not exist\n\n", arg_sf_in); goto print_usage; }
if (!sfo_riffchunk_read(NULL, &chunkHead, f_sf_in) || !SFO_FourCCEquals(chunkHead.id, "sfbk"))
{
fprintf(stderr, "Error: Passed input file '%s' is not a valid soundfont file\n\n", arg_sf_in);
goto print_usage;
}
while (sfo_riffchunk_read(&chunkHead, &chunkList, f_sf_in))
{
unsigned int pos_listsize = (unsigned int)ftell(f_sf_in) - 8;
if (!SFO_FourCCEquals(chunkList.id, "sdta"))
{
fseek(f_sf_in, chunkList.size, SEEK_CUR);
continue;
}
for (; sfo_riffchunk_read(&chunkList, &chunk, f_sf_in); fseek(f_sf_in, chunkList.size, SEEK_CUR))
{
int is_pcm = SFO_FourCCEquals(chunk.id, "smpl");
if (!is_pcm && !SFO_FourCCEquals(chunk.id, "smpo"))
continue;
printf("Soundfont file '%s' contains a %s sample stream\n", arg_sf_in, (is_pcm ? "PCM" : "OGG"));
if (ext_sf_in != '2' && ext_sf_in != 'o') printf(" Warning: Soundfont file has unknown file extension (should be .SF2 or .SFO)\n");
if (ext_sf_in == '2' && !is_pcm) printf(" Warning: Soundfont file has .SF%c extension but sample stream is %s\n", '2', "OGG (should be .SFO)");
if (ext_sf_in == 'o' && is_pcm) printf(" Warning: Soundfont file has .SF%c extension but sample stream is %s\n", 'O', "PCM (should be .SF2)");
if (arg_sf_out)
{
unsigned int pos_smpchunk, end_smpchunk, len_smpl, end_sf, len_list_in, len_list_out;
printf("Writing file '%s' with samples from '%s'\n", arg_sf_out, arg_smpl);
if (ext_sf_out != '2' && ext_sf_out != 'o') printf(" Warning: Soundfont file has unknown file extension (should be .SF2 or .SFO)\n");
if (ext_smpl != 'v' && ext_smpl != 'g') printf(" Warning: Sample file has unknown file extension (should be .WAV or .OGG)\n");
if (ext_sf_out == '2' && ext_smpl != 'v') printf(" Warning: Soundfont file has .SF%c extension but sample file is .%s\n", '2', "OGG");
if (ext_sf_out == 'o' && ext_smpl == 'v') printf(" Warning: Soundfont file has .SF%c extension but sample file is .%s\n", 'O', "WAV");
f_smpl = fopen(arg_smpl, "rb");
if (!f_smpl) { fprintf(stderr, "Error: Unable to open input file '%s'\n\n", arg_smpl); goto print_usage; }
if (ext_smpl == 'v')
{
struct sfo_wavheader wav_hdr;
fread(&wav_hdr, sizeof(wav_hdr), 1, f_smpl);
if (!SFO_FourCCEquals(wav_hdr.Subchunk2ID, "data") || !SFO_FourCCEquals(wav_hdr.RIFF, "RIFF")
|| !SFO_FourCCEquals(wav_hdr.WAVE, "WAVE") || !SFO_FourCCEquals(wav_hdr.fmt, "fmt ")
|| wav_hdr.Subchunk1Size != 16 || wav_hdr.AudioFormat != 1 || wav_hdr.NumOfChan != 1
|| wav_hdr.bytesPerSec != wav_hdr.SamplesPerSec * sizeof(short) || wav_hdr.bitsPerSample != sizeof(short) * 8)
{ fprintf(stderr, "Input .WAV file is not a valid raw PCM encoded wave file\n\n"); goto print_usage; }
len_smpl = wav_hdr.Subchunk2Size;
}
else
{
fseek(f_smpl, 0, SEEK_END);
len_smpl = (unsigned int)ftell(f_smpl);
fseek(f_smpl, 0, SEEK_SET);
}
f_sf_out = fopen(arg_sf_out, "wb");
if (!f_sf_out) { fprintf(stderr, "Error: Unable to open output file '%s'\n\n", arg_sf_out); goto print_usage; }
pos_smpchunk = (unsigned int)(ftell(f_sf_in) - sizeof(struct sfo_riffchunk));
end_smpchunk = pos_smpchunk + (unsigned int)sizeof(struct sfo_riffchunk) + chunk.size;
fseek(f_sf_in, 0, SEEK_END);
end_sf = (unsigned int)ftell(f_sf_in);
/* Write data before list chunk size */
fseek(f_sf_in, 0, SEEK_SET);
sfo_copy(f_sf_in, f_sf_out, pos_listsize);
/* Write new list chunk size */
fread(&len_list_in, 4, 1, f_sf_in);
len_list_out = len_list_in - chunk.size + len_smpl;
fwrite(&len_list_out, 4, 1, f_sf_out);
/* Write data until sample chunk */
sfo_copy(f_sf_in, f_sf_out, pos_smpchunk - pos_listsize - 4);
/* Write sample chunk */
fwrite((ext_smpl == 'v' ? "smpl" : "smpo"), 4, 1, f_sf_out);
fwrite(&len_smpl, 4, 1, f_sf_out);
sfo_copy(f_smpl, f_sf_out, len_smpl);
fclose(f_smpl);
/* Write data after sample chunk */
fseek(f_sf_in, end_smpchunk, SEEK_SET);
sfo_copy(f_sf_in, f_sf_out, end_sf - end_smpchunk);
fclose(f_sf_out);
}
else if (arg_smpl)
{
f_smpl = fopen(arg_smpl, "wb");
printf("Writing file '%s' with %s sample stream\n", arg_smpl, (is_pcm ? "PCM" : "OGG"));
if (ext_smpl != 'v' && ext_smpl != 'g') printf(" Warning: Sample file has unknown file extension (should be .WAV or .OGG)\n");
if (ext_smpl == 'v' && !is_pcm) printf(" Warning: Sample file has .%s extension but sample stream is %s\n", "WAV", "OGG (should be .OGG)");
if (ext_smpl == 'g' && is_pcm) printf(" Warning: Sample file has .%s extension but sample stream is %s\n", "OGG", "PCM (should be .WAV)");
if (!f_smpl) { fprintf(stderr, "Unable to open output file '%s'\n\n", arg_smpl); goto print_usage; }
if (is_pcm)
{
struct sfo_wavheader wav_hdr;
memcpy(wav_hdr.Subchunk2ID, "data", 4);
memcpy(wav_hdr.RIFF, "RIFF", 4);
memcpy(wav_hdr.WAVE, "WAVE", 4);
memcpy(wav_hdr.fmt, "fmt ", 4);
wav_hdr.Subchunk1Size = 16;
wav_hdr.AudioFormat = 1;
wav_hdr.NumOfChan = 1;
wav_hdr.Subchunk2Size = (unsigned int)chunk.size;
wav_hdr.ChunkSize = sizeof(wav_hdr) - 4 - 4 + wav_hdr.Subchunk2Size;
wav_hdr.SamplesPerSec = 22050;
wav_hdr.bytesPerSec = 22050 * sizeof(short);
wav_hdr.blockAlign = 1 * sizeof(short);
wav_hdr.bitsPerSample = sizeof(short) * 8;
fwrite(&wav_hdr, sizeof(wav_hdr), 1, f_smpl);
}
sfo_copy(f_sf_in, f_smpl, chunk.size);
fclose(f_smpl);
printf("DONE\n");
}
fclose(f_sf_in);
return 0;
}
}
fprintf(stderr, "Passed input file is not a valid soundfont file\n\n");
goto print_usage;
}

View file

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sfotool", "sfotool.vcxproj", "{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Debug|Win32.ActiveCfg = Debug|Win32
{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Debug|Win32.Build.0 = Debug|Win32
{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Debug|x64.ActiveCfg = Debug|x64
{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Debug|x64.Build.0 = Debug|x64
{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Release|Win32.ActiveCfg = Release|Win32
{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Release|Win32.Build.0 = Release|Win32
{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Release|x64.ActiveCfg = Release|x64
{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{FFFFFFFF-FFFF-4FFF-FFFF-FFFFFFFFFFFF}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>sfotool</RootNamespace>
<ProjectName>sfotool</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '11.0' Or '$(PlatformToolsetVersion)' == '110' Or '$(MSBuildToolsVersion)' == '4.0'">v110_xp</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '12.0' Or '$(PlatformToolsetVersion)' == '120' Or '$(MSBuildToolsVersion)' == '12.0'">v120</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0' Or '$(PlatformToolsetVersion)' == '140' Or '$(MSBuildToolsVersion)' == '14.0'">v140</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0' Or '$(PlatformToolsetVersion)' == '141' Or '$(MSBuildToolsVersion)' == '15.0'">v141</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0' Or '$(PlatformToolsetVersion)' == '142' Or '$(MSBuildToolsVersion)' == '16.0'">v142</PlatformToolset>
<PlatformToolset Condition="'$(VisualStudioVersion)' == '17.0' Or '$(PlatformToolsetVersion)' == '143' Or '$(MSBuildToolsVersion)' == '17.0'">v143</PlatformToolset>
<PlatformToolset Condition="'$(PlatformToolset)' == ''">$(DefaultPlatformToolset)</PlatformToolset>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet>
<OutDir>$(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\</OutDir>
<IntDir>$(SolutionDir)$(Configuration)\$(ProjectName)_$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<IntrinsicFunctions>true</IntrinsicFunctions>
<ExceptionHandling>false</ExceptionHandling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateMapFile>true</GenerateMapFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions Condition="'$(VisualStudioVersion)' &gt;= '12.0' Or '$(PlatformToolsetVersion)' &gt;= '120' Or '$(MSBuildToolsVersion)' &gt;= '12.0'">/Gw %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ItemGroup>
<ClCompile Include="main.c" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,531 @@
/* TinyMidiLoader - v0.7 - Minimalistic midi parsing library - https://github.com/schellingb/TinySoundFont
no warranty implied; use at your own risk
Do this:
#define TML_IMPLEMENTATION
before you include this file in *one* C or C++ file to create the implementation.
// i.e. it should look like this:
#include ...
#include ...
#define TML_IMPLEMENTATION
#include "tml.h"
[OPTIONAL] #define TML_NO_STDIO to remove stdio dependency
[OPTIONAL] #define TML_MALLOC, TML_REALLOC, and TML_FREE to avoid stdlib.h
[OPTIONAL] #define TML_MEMCPY to avoid string.h
LICENSE (ZLIB)
Copyright (C) 2017, 2018, 2020 Bernhard Schelling
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 TML_INCLUDE_TML_INL
#define TML_INCLUDE_TML_INL
#ifdef __cplusplus
extern "C" {
#endif
// Define this if you want the API functions to be static
#ifdef TML_STATIC
#define TMLDEF static
#else
#define TMLDEF extern
#endif
// Channel message type
enum TMLMessageType
{
TML_NOTE_OFF = 0x80, TML_NOTE_ON = 0x90, TML_KEY_PRESSURE = 0xA0, TML_CONTROL_CHANGE = 0xB0, TML_PROGRAM_CHANGE = 0xC0, TML_CHANNEL_PRESSURE = 0xD0, TML_PITCH_BEND = 0xE0, TML_SET_TEMPO = 0x51
};
// Midi controller numbers
enum TMLController
{
TML_BANK_SELECT_MSB, TML_MODULATIONWHEEL_MSB, TML_BREATH_MSB, TML_FOOT_MSB = 4, TML_PORTAMENTO_TIME_MSB, TML_DATA_ENTRY_MSB, TML_VOLUME_MSB,
TML_BALANCE_MSB, TML_PAN_MSB = 10, TML_EXPRESSION_MSB, TML_EFFECTS1_MSB, TML_EFFECTS2_MSB, TML_GPC1_MSB = 16, TML_GPC2_MSB, TML_GPC3_MSB, TML_GPC4_MSB,
TML_BANK_SELECT_LSB = 32, TML_MODULATIONWHEEL_LSB, TML_BREATH_LSB, TML_FOOT_LSB = 36, TML_PORTAMENTO_TIME_LSB, TML_DATA_ENTRY_LSB, TML_VOLUME_LSB,
TML_BALANCE_LSB, TML_PAN_LSB = 42, TML_EXPRESSION_LSB, TML_EFFECTS1_LSB, TML_EFFECTS2_LSB, TML_GPC1_LSB = 48, TML_GPC2_LSB, TML_GPC3_LSB, TML_GPC4_LSB,
TML_SUSTAIN_SWITCH = 64, TML_PORTAMENTO_SWITCH, TML_SOSTENUTO_SWITCH, TML_SOFT_PEDAL_SWITCH, TML_LEGATO_SWITCH, TML_HOLD2_SWITCH,
TML_SOUND_CTRL1, TML_SOUND_CTRL2, TML_SOUND_CTRL3, TML_SOUND_CTRL4, TML_SOUND_CTRL5, TML_SOUND_CTRL6,
TML_SOUND_CTRL7, TML_SOUND_CTRL8, TML_SOUND_CTRL9, TML_SOUND_CTRL10, TML_GPC5, TML_GPC6, TML_GPC7, TML_GPC8,
TML_PORTAMENTO_CTRL, TML_FX_REVERB = 91, TML_FX_TREMOLO, TML_FX_CHORUS, TML_FX_CELESTE_DETUNE, TML_FX_PHASER,
TML_DATA_ENTRY_INCR, TML_DATA_ENTRY_DECR, TML_NRPN_LSB, TML_NRPN_MSB, TML_RPN_LSB, TML_RPN_MSB,
TML_ALL_SOUND_OFF = 120, TML_ALL_CTRL_OFF, TML_LOCAL_CONTROL, TML_ALL_NOTES_OFF, TML_OMNI_OFF, TML_OMNI_ON, TML_POLY_OFF, TML_POLY_ON
};
// A single MIDI message linked to the next message in time
typedef struct tml_message
{
// Time of the message in milliseconds
unsigned int time;
// Type (see TMLMessageType) and channel number
unsigned char type, channel;
// 2 byte of parameter data based on the type:
// - key, velocity for TML_NOTE_ON and TML_NOTE_OFF messages
// - key, key_pressure for TML_KEY_PRESSURE messages
// - control, control_value for TML_CONTROL_CHANGE messages (see TMLController)
// - program for TML_PROGRAM_CHANGE messages
// - channel_pressure for TML_CHANNEL_PRESSURE messages
// - pitch_bend for TML_PITCH_BEND messages
union
{
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4201) //nonstandard extension used: nameless struct/union
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" //ISO C++ prohibits anonymous structs
#endif
struct { union { char key, control, program, channel_pressure; }; union { char velocity, key_pressure, control_value; }; };
struct { unsigned short pitch_bend; };
#ifdef _MSC_VER
#pragma warning( pop )
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
};
// The pointer to the next message in time following this event
struct tml_message* next;
} tml_message;
// The load functions will return a pointer to a struct tml_message.
// Normally the linked list gets traversed by following the next pointers.
// Make sure to keep the pointer to the first message to free the memory.
// On error the tml_load* functions will return NULL most likely due to an
// invalid MIDI stream (or if the file did not exist in tml_load_filename).
#ifndef TML_NO_STDIO
// Directly load a MIDI file from a .mid file path
TMLDEF tml_message* tml_load_filename(const char* filename);
#endif
// Load a MIDI file from a block of memory
TMLDEF tml_message* tml_load_memory(const void* buffer, int size);
// Get infos about this loaded MIDI file, returns the note count
// NULL can be passed for any output value pointer if not needed.
// used_channels: Will be set to how many channels play notes
// (i.e. 1 if channel 15 is used but no other)
// used_programs: Will be set to how many different programs are used
// total_notes: Will be set to the total number of note on messages
// time_first_note: Will be set to the time of the first note on message
// time_length: Will be set to the total time in milliseconds
TMLDEF int tml_get_info(tml_message* first_message, int* used_channels, int* used_programs, int* total_notes, unsigned int* time_first_note, unsigned int* time_length);
// Read the tempo (microseconds per quarter note) value from a message with the type TML_SET_TEMPO
TMLDEF int tml_get_tempo_value(tml_message* set_tempo_message);
// Free all the memory of the linked message list (can also call free() manually)
TMLDEF void tml_free(tml_message* f);
// Stream structure for the generic loading
struct tml_stream
{
// Custom data given to the functions as the first parameter
void* data;
// Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes)
int (*read)(void* data, void* ptr, unsigned int size);
};
// Generic Midi loading method using the stream structure above
TMLDEF tml_message* tml_load(struct tml_stream* stream);
// If this library is used together with TinySoundFont, tsf_stream (equivalent to tml_stream) can also be used
struct tsf_stream;
TMLDEF tml_message* tml_load_tsf_stream(struct tsf_stream* stream);
#ifdef __cplusplus
}
#endif
// end header
// ---------------------------------------------------------------------------------------------------------
#endif //TML_INCLUDE_TML_INL
#ifdef TML_IMPLEMENTATION
#if !defined(TML_MALLOC) || !defined(TML_FREE) || !defined(TML_REALLOC)
# include <stdlib.h>
# define TML_MALLOC malloc
# define TML_FREE free
# define TML_REALLOC realloc
#endif
#if !defined(TML_MEMCPY)
# include <string.h>
# define TML_MEMCPY memcpy
#endif
#ifndef TML_NO_STDIO
# include <stdio.h>
#endif
#define TML_NULL 0
////crash on errors and warnings to find broken midi files while debugging
//#define TML_ERROR(msg) *(int*)0 = 0xbad;
//#define TML_WARN(msg) *(int*)0 = 0xf00d;
////print errors and warnings
//#define TML_ERROR(msg) printf("ERROR: %s\n", msg);
//#define TML_WARN(msg) printf("WARNING: %s\n", msg);
#ifndef TML_ERROR
#define TML_ERROR(msg)
#endif
#ifndef TML_WARN
#define TML_WARN(msg)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef TML_NO_STDIO
static int tml_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); }
TMLDEF tml_message* tml_load_filename(const char* filename)
{
struct tml_message* res;
struct tml_stream stream = { TML_NULL, (int(*)(void*,void*,unsigned int))&tml_stream_stdio_read };
#if __STDC_WANT_SECURE_LIB__
FILE* f = TML_NULL; fopen_s(&f, filename, "rb");
#else
FILE* f = fopen(filename, "rb");
#endif
if (!f) { TML_ERROR("File not found"); return 0; }
stream.data = f;
res = tml_load(&stream);
fclose(f);
return res;
}
#endif
struct tml_stream_memory { const char* buffer; unsigned int total, pos; };
static int tml_stream_memory_read(struct tml_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TML_MEMCPY(ptr, m->buffer+m->pos, size); m->pos += size; return size; }
TMLDEF struct tml_message* tml_load_memory(const void* buffer, int size)
{
struct tml_stream stream = { TML_NULL, (int(*)(void*,void*,unsigned int))&tml_stream_memory_read };
struct tml_stream_memory f = { 0, 0, 0 };
f.buffer = (const char*)buffer;
f.total = size;
stream.data = &f;
return tml_load(&stream);
}
struct tml_track
{
unsigned int Idx, End, Ticks;
};
struct tml_tempomsg
{
unsigned int time;
unsigned char type, Tempo[3];
tml_message* next;
};
struct tml_parser
{
unsigned char *buf, *buf_end;
int last_status, message_array_size, message_count;
};
enum TMLSystemType
{
TML_TEXT = 0x01, TML_COPYRIGHT = 0x02, TML_TRACK_NAME = 0x03, TML_INST_NAME = 0x04, TML_LYRIC = 0x05, TML_MARKER = 0x06, TML_CUE_POINT = 0x07,
TML_EOT = 0x2f, TML_SMPTE_OFFSET = 0x54, TML_TIME_SIGNATURE = 0x58, TML_KEY_SIGNATURE = 0x59, TML_SEQUENCER_EVENT = 0x7f,
TML_SYSEX = 0xf0, TML_TIME_CODE = 0xf1, TML_SONG_POSITION = 0xf2, TML_SONG_SELECT = 0xf3, TML_TUNE_REQUEST = 0xf6, TML_EOX = 0xf7, TML_SYNC = 0xf8,
TML_TICK = 0xf9, TML_START = 0xfa, TML_CONTINUE = 0xfb, TML_STOP = 0xfc, TML_ACTIVE_SENSING = 0xfe, TML_SYSTEM_RESET = 0xff
};
static int tml_readbyte(struct tml_parser* p)
{
return (p->buf == p->buf_end ? -1 : *(p->buf++));
}
static int tml_readvariablelength(struct tml_parser* p)
{
unsigned int res = 0, i = 0;
unsigned char c;
for (; i != 4; i++)
{
if (p->buf == p->buf_end) { TML_WARN("Unexpected end of file"); return -1; }
c = *(p->buf++);
if (c & 0x80) res = ((res | (c & 0x7F)) << 7);
else return (int)(res | c);
}
TML_WARN("Invalid variable length byte count"); return -1;
}
static int tml_parsemessage(tml_message** f, struct tml_parser* p)
{
int deltatime = tml_readvariablelength(p), status = tml_readbyte(p);
tml_message* evt;
if (deltatime & 0xFFF00000) deltatime = 0; //throw away delays that are insanely high for malformatted midis
if (status < 0) { TML_WARN("Unexpected end of file"); return -1; }
if ((status & 0x80) == 0)
{
// Invalid, use same status as before
if ((p->last_status & 0x80) == 0) { TML_WARN("Undefined status and invalid running status"); return -1; }
p->buf--;
status = p->last_status;
}
else p->last_status = status;
if (p->message_array_size == p->message_count)
{
//start allocated memory size of message array at 64, double each time until 8192, then add 1024 entries until done
p->message_array_size += (!p->message_array_size ? 64 : (p->message_array_size > 4096 ? 1024 : p->message_array_size));
*f = (tml_message*)TML_REALLOC(*f, p->message_array_size * sizeof(tml_message));
if (!*f) { TML_ERROR("Out of memory"); return -1; }
}
evt = *f + p->message_count;
//check what message we have
if ((status == TML_SYSEX) || (status == TML_EOX)) //sysex
{
//sysex messages are not handled
p->buf += tml_readvariablelength(p);
if (p->buf > p->buf_end) { TML_WARN("Unexpected end of file"); p->buf = p->buf_end; return -1; }
evt->type = 0;
}
else if (status == 0xFF) //meta events
{
int meta_type = tml_readbyte(p), buflen = tml_readvariablelength(p);
unsigned char* metadata = p->buf;
if (meta_type < 0) { TML_WARN("Unexpected end of file"); return -1; }
if (buflen > 0 && (p->buf += buflen) > p->buf_end) { TML_WARN("Unexpected end of file"); p->buf = p->buf_end; return -1; }
switch (meta_type)
{
case TML_EOT:
if (buflen != 0) { TML_WARN("Invalid length for EndOfTrack event"); return -1; }
if (!deltatime) return TML_EOT; //no need to store this message
evt->type = TML_EOT;
break;
case TML_SET_TEMPO:
if (buflen != 3) { TML_WARN("Invalid length for SetTempo meta event"); return -1; }
evt->type = TML_SET_TEMPO;
((struct tml_tempomsg*)evt)->Tempo[0] = metadata[0];
((struct tml_tempomsg*)evt)->Tempo[1] = metadata[1];
((struct tml_tempomsg*)evt)->Tempo[2] = metadata[2];
break;
default:
evt->type = 0;
}
}
else //channel message
{
int param;
if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; }
evt->key = (param & 0x7f);
evt->channel = (status & 0x0f);
switch (evt->type = (status & 0xf0))
{
case TML_NOTE_OFF:
case TML_NOTE_ON:
case TML_KEY_PRESSURE:
case TML_CONTROL_CHANGE:
if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; }
evt->velocity = (param & 0x7f);
break;
case TML_PITCH_BEND:
if ((param = tml_readbyte(p)) < 0) { TML_WARN("Unexpected end of file"); return -1; }
evt->pitch_bend = ((param & 0x7f) << 7) | evt->key;
break;
case TML_PROGRAM_CHANGE:
case TML_CHANNEL_PRESSURE:
evt->velocity = 0;
break;
default: //ignore system/manufacture messages
evt->type = 0;
break;
}
}
if (deltatime || evt->type)
{
evt->time = deltatime;
p->message_count++;
}
return evt->type;
}
TMLDEF tml_message* tml_load(struct tml_stream* stream)
{
int num_tracks, division, trackbufsize = 0;
unsigned char midi_header[14], *trackbuf = TML_NULL;
struct tml_message* messages = TML_NULL;
struct tml_track *tracks, *t, *tracksEnd;
struct tml_parser p = { TML_NULL, TML_NULL, 0, 0, 0 };
// Parse MIDI header
if (stream->read(stream->data, midi_header, 14) != 14) { TML_ERROR("Unexpected end of file"); return messages; }
if (midi_header[0] != 'M' || midi_header[1] != 'T' || midi_header[2] != 'h' || midi_header[3] != 'd' ||
midi_header[7] != 6 || midi_header[9] > 2) { TML_ERROR("Doesn't look like a MIDI file: invalid MThd header"); return messages; }
if (midi_header[12] & 0x80) { TML_ERROR("File uses unsupported SMPTE timing"); return messages; }
num_tracks = (int)(midi_header[10] << 8) | midi_header[11];
division = (int)(midi_header[12] << 8) | midi_header[13]; //division is ticks per beat (quarter-note)
if (num_tracks <= 0 && division <= 0) { TML_ERROR("Doesn't look like a MIDI file: invalid track or division values"); return messages; }
// Allocate temporary tracks array for parsing
tracks = (struct tml_track*)TML_MALLOC(sizeof(struct tml_track) * num_tracks);
tracksEnd = &tracks[num_tracks];
for (t = tracks; t != tracksEnd; t++) t->Idx = t->End = t->Ticks = 0;
// Read all messages for all tracks
for (t = tracks; t != tracksEnd; t++)
{
unsigned char track_header[8];
int track_length;
if (stream->read(stream->data, track_header, 8) != 8) { TML_WARN("Unexpected end of file"); break; }
if (track_header[0] != 'M' || track_header[1] != 'T' || track_header[2] != 'r' || track_header[3] != 'k')
{ TML_WARN("Invalid MTrk header"); break; }
// Get size of track data and read into buffer (allocate bigger buffer if needed)
track_length = track_header[7] | (track_header[6] << 8) | (track_header[5] << 16) | (track_header[4] << 24);
if (track_length < 0) { TML_WARN("Invalid MTrk header"); break; }
if (trackbufsize < track_length) { TML_FREE(trackbuf); trackbuf = (unsigned char*)TML_MALLOC(trackbufsize = track_length); }
if (stream->read(stream->data, trackbuf, track_length) != track_length) { TML_WARN("Unexpected end of file"); break; }
t->Idx = p.message_count;
for (p.buf_end = (p.buf = trackbuf) + track_length; p.buf != p.buf_end;)
{
int type = tml_parsemessage(&messages, &p);
if (type == TML_EOT || type < 0) break; //file end or illegal data encountered
}
if (p.buf != p.buf_end) { TML_WARN( "Track length did not match data length"); }
t->End = p.message_count;
}
TML_FREE(trackbuf);
// Change message time signature from delta ticks to actual msec values and link messages ordered by time
if (p.message_count)
{
tml_message *PrevMessage = TML_NULL, *Msg, *MsgEnd, Swap;
unsigned int ticks = 0, tempo_ticks = 0; //tick counter and value at last tempo change
int step_smallest, msec, tempo_msec = 0; //msec value at last tempo change
double ticks2time = 500000 / (1000.0 * division); //milliseconds per tick
// Loop through all messages over all tracks ordered by time
for (step_smallest = 0; step_smallest != 0x7fffffff; ticks += step_smallest)
{
step_smallest = 0x7fffffff;
msec = tempo_msec + (int)((ticks - tempo_ticks) * ticks2time);
for (t = tracks; t != tracksEnd; t++)
{
if (t->Idx == t->End) continue;
for (Msg = &messages[t->Idx], MsgEnd = &messages[t->End]; Msg != MsgEnd && t->Ticks + Msg->time == ticks; Msg++, t->Idx++)
{
t->Ticks += Msg->time;
if (Msg->type == TML_SET_TEMPO)
{
unsigned char* Tempo = ((struct tml_tempomsg*)Msg)->Tempo;
ticks2time = ((Tempo[0]<<16)|(Tempo[1]<<8)|Tempo[2])/(1000.0 * division);
tempo_msec = msec;
tempo_ticks = ticks;
}
if (Msg->type)
{
Msg->time = msec;
if (PrevMessage) { PrevMessage->next = Msg; PrevMessage = Msg; }
else { Swap = *Msg; *Msg = *messages; *messages = Swap; PrevMessage = messages; }
}
}
if (Msg != MsgEnd && t->Ticks + Msg->time > ticks)
{
int step = (int)(t->Ticks + Msg->time - ticks);
if (step < step_smallest) step_smallest = step;
}
}
}
if (PrevMessage) PrevMessage->next = TML_NULL;
else p.message_count = 0;
}
TML_FREE(tracks);
if (p.message_count == 0)
{
TML_FREE(messages);
messages = TML_NULL;
}
return messages;
}
TMLDEF tml_message* tml_load_tsf_stream(struct tsf_stream* stream)
{
return tml_load((struct tml_stream*)stream);
}
TMLDEF int tml_get_info(tml_message* Msg, int* out_used_channels, int* out_used_programs, int* out_total_notes, unsigned int* out_time_first_note, unsigned int* out_time_length)
{
int used_programs = 0, used_channels = 0, total_notes = 0;
unsigned int time_first_note = 0xffffffff, time_length = 0;
unsigned char channels[16] = { 0 }, programs[128] = { 0 };
for (;Msg; Msg = Msg->next)
{
time_length = Msg->time;
if (Msg->type == TML_PROGRAM_CHANGE && !programs[(int)Msg->program]) { programs[(int)Msg->program] = 1; used_programs++; }
if (Msg->type != TML_NOTE_ON) continue;
if (time_first_note == 0xffffffff) time_first_note = time_length;
if (!channels[Msg->channel]) { channels[Msg->channel] = 1; used_channels++; }
total_notes++;
}
if (time_first_note == 0xffffffff) time_first_note = 0;
if (out_used_channels ) *out_used_channels = used_channels;
if (out_used_programs ) *out_used_programs = used_programs;
if (out_total_notes ) *out_total_notes = total_notes;
if (out_time_first_note) *out_time_first_note = time_first_note;
if (out_time_length ) *out_time_length = time_length;
return total_notes;
}
TMLDEF int tml_get_tempo_value(tml_message* msg)
{
unsigned char* Tempo;
if (!msg || msg->type != TML_SET_TEMPO) return 0;
Tempo = ((struct tml_tempomsg*)msg)->Tempo;
return ((Tempo[0]<<16)|(Tempo[1]<<8)|Tempo[2]);
}
TMLDEF void tml_free(tml_message* f)
{
TML_FREE(f);
}
#ifdef __cplusplus
}
#endif
#endif //TML_IMPLEMENTATION

File diff suppressed because it is too large Load diff

@ -1 +0,0 @@
Subproject commit de399881c65c438a635627c749440eeea7e05599

View file

@ -0,0 +1,15 @@
name: build
on: [push, pull_request]
jobs:
generic:
strategy:
matrix:
os: [windows, ubuntu, macos]
name: ${{matrix.os}}
runs-on: ${{matrix.os}}-latest
steps:
- uses: actions/checkout@v1
- name: test
run: cd test && python test_all.py

View file

@ -0,0 +1,7 @@
Copyright (c) 2018-2021 Johannes Kuhlmann
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.

161
source/engine/thirdparty/cgltf/README.md vendored Normal file
View file

@ -0,0 +1,161 @@
# :diamond_shape_with_a_dot_inside: cgltf
**Single-file/stb-style C glTF loader and writer**
[![Build Status](https://github.com/jkuhlmann/cgltf/workflows/build/badge.svg)](https://github.com/jkuhlmann/cgltf/actions)
Used in: [bgfx](https://github.com/bkaradzic/bgfx), [Filament](https://github.com/google/filament), [gltfpack](https://github.com/zeux/meshoptimizer/tree/master/gltf), [raylib](https://github.com/raysan5/raylib), [Unigine](https://developer.unigine.com/en/docs/2.14.1/third_party?rlang=cpp#cgltf), and more!
## Usage: Loading
Loading from file:
```c
#define CGLTF_IMPLEMENTATION
#include "cgltf.h"
cgltf_options options = {0};
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, "scene.gltf", &data);
if (result == cgltf_result_success)
{
/* TODO make awesome stuff */
cgltf_free(data);
}
```
Loading from memory:
```c
#define CGLTF_IMPLEMENTATION
#include "cgltf.h"
void* buf; /* Pointer to glb or gltf file data */
size_t size; /* Size of the file data */
cgltf_options options = {0};
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse(&options, buf, size, &data);
if (result == cgltf_result_success)
{
/* TODO make awesome stuff */
cgltf_free(data);
}
```
Note that cgltf does not load the contents of extra files such as buffers or images into memory by default. You'll need to read these files yourself using URIs from `data.buffers[]` or `data.images[]` respectively.
For buffer data, you can alternatively call `cgltf_load_buffers`, which will use `FILE*` APIs to open and read buffer files. This automatically decodes base64 data URIs in buffers. For data URIs in images, you will need to use `cgltf_load_buffer_base64`.
**For more in-depth documentation and a description of the public interface refer to the top of the `cgltf.h` file.**
## Usage: Writing
When writing glTF data, you need a valid `cgltf_data` structure that represents a valid glTF document. You can construct such a structure yourself or load it using the loader functions described above. The writer functions do not deallocate any memory. So, you either have to do it manually or call `cgltf_free()` if you got the data by loading it from a glTF document.
Writing to file:
```c
#define CGLTF_IMPLEMENTATION
#define CGLTF_WRITE_IMPLEMENTATION
#include "cgltf_write.h"
cgltf_options options = {0};
cgltf_data* data = /* TODO must be valid data */;
cgltf_result result = cgltf_write_file(&options, "out.gltf", data);
if (result != cgltf_result_success)
{
/* TODO handle error */
}
```
Writing to memory:
```c
#define CGLTF_IMPLEMENTATION
#define CGLTF_WRITE_IMPLEMENTATION
#include "cgltf_write.h"
cgltf_options options = {0};
cgltf_data* data = /* TODO must be valid data */;
cgltf_size size = cgltf_write(&options, NULL, 0, data);
char* buf = malloc(size);
cgltf_size written = cgltf_write(&options, buf, size, data);
if (written != size)
{
/* TODO handle error */
}
```
Note that cgltf does not write the contents of extra files such as buffers or images. You'll need to write this data yourself.
**For more in-depth documentation and a description of the public interface refer to the top of the `cgltf_write.h` file.**
## Features
cgltf supports core glTF 2.0:
- glb (binary files) and gltf (JSON files)
- meshes (including accessors, buffer views, buffers)
- materials (including textures, samplers, images)
- scenes and nodes
- skins
- animations
- cameras
- morph targets
- extras data
cgltf also supports some glTF extensions:
- EXT_mesh_gpu_instancing
- EXT_meshopt_compression
- KHR_draco_mesh_compression (requires a library like [Google's Draco](https://github.com/google/draco) for decompression though)
- KHR_lights_punctual
- KHR_materials_clearcoat
- KHR_materials_emissive_strength
- KHR_materials_ior
- KHR_materials_iridescence
- KHR_materials_pbrSpecularGlossiness
- KHR_materials_sheen
- KHR_materials_specular
- KHR_materials_transmission
- KHR_materials_unlit
- KHR_materials_variants
- KHR_materials_volume
- KHR_texture_basisu (requires a library like [Binomial Basisu](https://github.com/BinomialLLC/basis_universal) for transcoding to native compressed texture)
- KHR_texture_transform
cgltf does **not** yet support unlisted extensions. However, unlisted extensions can be accessed via "extensions" member on objects.
## Building
The easiest approach is to integrate the `cgltf.h` header file into your project. If you are unfamiliar with single-file C libraries (also known as stb-style libraries), this is how it goes:
1. Include `cgltf.h` where you need the functionality.
1. Have exactly one source file that defines `CGLTF_IMPLEMENTATION` before including `cgltf.h`.
1. Use the cgltf functions as described above.
Support for writing can be found in a separate file called `cgltf_write.h` (which includes `cgltf.h`). Building it works analogously using the `CGLTF_WRITE_IMPLEMENTATION` define.
## Contributing
Everyone is welcome to contribute to the library. If you find any problems, you can submit them using [GitHub's issue system](https://github.com/jkuhlmann/cgltf/issues). If you want to contribute code, you should fork the project and then send a pull request.
## Dependencies
None.
C headers being used by the implementation:
```
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <assert.h> // If asserts are enabled.
```
Note, this library has a copy of the [JSMN JSON parser](https://github.com/zserge/jsmn) embedded in its source.
## Testing
There is a Python script in the `test/` folder that retrieves the glTF 2.0 sample files from the glTF-Sample-Models repository (https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0) and runs the library against all gltf and glb files.
Here's one way to build and run the test:
cd test ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Debug
make -j
cd ..
./test_all.py
There is also a llvm-fuzz test in `fuzz/`. See http://llvm.org/docs/LibFuzzer.html for more information.

6739
source/engine/thirdparty/cgltf/cgltf.h vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
{"textures":[{"extensions":{"KHR_texture_basisu":{""}}:{""""},""}]}

Binary file not shown.

View file

@ -0,0 +1,53 @@
{
"scenes" : [
{
"nodes" : [ 0 ]
}
],
"nodes" : [
{
"mesh" : 0
}
],
"meshes" : [
{
"primitives" : [ {
"attributes" : {
"POSITION" : 0
}
} ]
}
],
"buffers" : [
{
"uri" : "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA",
"byteLength" : 36
}
],
"bufferViews" : [
{
"buffer" : 0,
"byteOffset" : 0,
"byteLength" : 36,
"target" : 34962
}
],
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 3,
"type" : "VEC3",
"max" : [ 1.0, 1.0, 0.0 ],
"min" : [ 0.0, 0.0, 0.0 ]
}
],
"asset" : {
"version" : "2.0"
}
}

View file

@ -0,0 +1,172 @@
{
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 2549,
"type": "VEC2"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 2549,
"type": "VEC3"
},
{
"bufferView": 2,
"componentType": 5126,
"count": 2549,
"type": "VEC4"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 2549,
"type": "VEC3",
"max": [
0.05445001,
0.130220339,
0.0544500239
],
"min": [
-0.05445001,
-0.130220339,
-0.0544500239
]
},
{
"bufferView": 4,
"componentType": 5123,
"count": 13530,
"type": "SCALAR"
}
],
"asset": {
"generator": "glTF Tools for Unity",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 20392
},
{
"buffer": 0,
"byteOffset": 20392,
"byteLength": 30588
},
{
"buffer": 0,
"byteOffset": 50980,
"byteLength": 40784
},
{
"buffer": 0,
"byteOffset": 91764,
"byteLength": 30588
},
{
"buffer": 0,
"byteOffset": 122352,
"byteLength": 27060
}
],
"buffers": [
{
"uri": "WaterBottle.bin",
"byteLength": 149412
}
],
"images": [
{
"uri": "WaterBottle_baseColor.png"
},
{
"uri": "WaterBottle_occlusionRoughnessMetallic.png"
},
{
"uri": "WaterBottle_normal.png"
},
{
"uri": "WaterBottle_emissive.png"
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"TEXCOORD_0": 0,
"NORMAL": 1,
"TANGENT": 2,
"POSITION": 3
},
"indices": 4,
"material": 0
}
],
"name": "WaterBottle"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 0
},
"metallicRoughnessTexture": {
"index": 1
}
},
"normalTexture": {
"index": 2
},
"occlusionTexture": {
"index": 1
},
"emissiveFactor": [
1.0,
1.0,
1.0
],
"emissiveTexture": {
"index": 3
},
"name": "BottleMat"
}
],
"nodes": [
{
"mesh": 0,
"rotation": [
0.0,
1.0,
0.0,
0.0
],
"name": "WaterBottle"
}
],
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"textures": [
{
"source": 0
},
{
"source": 1
},
{
"source": 2
},
{
"source": 3
}
]
}

View file

@ -0,0 +1,224 @@
#
# AFL dictionary for JSON
# -----------------------
#
# Just the very basics.
#
# Inspired by a dictionary by Jakub Wilk <jwilk@jwilk.net>
#
"0"
",0"
":0"
"0:"
"-1.2e+3"
"true"
"false"
"null"
"\"\""
",\"\""
":\"\""
"\"\":"
"{}"
",{}"
":{}"
"{\"\":0}"
"{{}}"
"[]"
",[]"
":[]"
"[0]"
"[[]]"
"''"
"\\"
"\\b"
"\\f"
"\\n"
"\\r"
"\\t"
"\\u0000"
"\\x00"
"\\0"
"\\uD800\\uDC00"
"\\uDBFF\\uDFFF"
"\"\":0"
"//"
"/**/"
#
# AFL dictionary for GLTF core
# -----------------------
"5120"
"5121"
"5122"
"5123"
"5125"
"5126"
"\"BLEND\""
"\"CUBICSPLINE\""
"\"LINEAR\""
"\"MASK\""
"\"MAT2\""
"\"MAT3\""
"\"MAT4\""
"\"OPAQUE\""
"\"SCALAR\""
"\"STEP\""
"\"VEC2\""
"\"VEC3\""
"\"VEC4\""
"\"accessor\""
"\"accessors\""
"\"alphaCutoff\""
"\"alphaMode\""
"\"animations\""
"\"aspectRatio\""
"\"asset\""
"\"attributes\""
"\"baseColorFactor\""
"\"baseColorTexture\""
"\"bufferView\""
"\"bufferViews\""
"\"buffer\""
"\"buffers\""
"\"byteLength\""
"\"byteOffset\""
"\"byteStride\""
"\"camera\""
"\"cameras\""
"\"channel\""
"\"channels\""
"\"children\""
"\"componentType\""
"\"copyright\""
"\"count\""
"\"doubleSided\""
"\"emissiveFactor\""
"\"emissiveTexture\""
"\"extensionsRequired\""
"\"extensionsUsed\""
"\"extensions\""
"\"extras\""
"\"generator\""
"\"image\""
"\"images\""
"\"index\""
"\"indices\""
"\"input\""
"\"interpolation\""
"\"inverseBindMatrices\""
"\"joints\""
"\"magFilter\""
"\"material\""
"\"materials\""
"\"matrix\""
"\"max\""
"\"mesh\""
"\"meshes\""
"\"metallicFactor\""
"\"metallicRoughnessTexture\""
"\"mimeType\""
"\"minFilter\""
"\"minVersion\""
"\"min\""
"\"mode\""
"\"name\""
"\"node\""
"\"nodes\""
"\"normalTextureInfo\""
"\"normalTexture\""
"\"normalized\""
"\"occlusionTextureInfo\""
"\"occlusionTexture\""
"\"orthographic\""
"\"output\""
"\"path\""
"\"pbrMetallicRoughness\""
"\"perspective\""
"\"primitive\""
"\"primitives\""
"\"rotation\""
"\"roughnessFactor\""
"\"sampler\""
"\"samplers\""
"\"scale\""
"\"scene\""
"\"scenes\""
"\"skeleton\""
"\"skin\""
"\"skins\""
"\"source\""
"\"sparse\""
"\"strength\""
"\"target\""
"\"targets\""
"\"texCoord\""
"\"textureInfo\""
"\"texture\""
"\"textures\""
"\"translation\""
"\"type\""
"\"uri\""
"\"values\""
"\"version\""
"\"weights\""
"\"wrapS\""
"\"wrapT\""
"\"xmag\""
"\"yfov\""
"\"ymag\""
"\"zfar\""
"\"znear\""
#
# AFL dictionary for GLTF extensions
# -----------------------
"\"KHR_materials_unlit\""
"\"KHR_texture_basisu\""
"\"KHR_materials_pbrSpecularGlossiness\""
"\"diffuseFactor\""
"\"diffuseTexture\""
"\"specularFactor\""
"\"glossinessFactor\""
"\"specularGlossinessTexture\""
"\"KHR_texture_transform\""
"\"offset\""
"\"rotation\""
"\"scale\""
"\"texCoord\""
"\"KHR_lights_punctual\""
"\"color\""
"\"intensity\""
"\"type\""
"\"range\""
"\"innerConeAngle\""
"\"outerConeAngle\""
"\"KHR_materials_transmission\""
"\"transmissionFactor\""
"\"transmissionTexture\""
"\"KHR_materials_volume\""
"\"thicknessFactor\""
"\"thicknessTexture\""
"\"attenuationColor\""
"\"attenuationDistance\""
"\"KHR_materials_sheen\""
"\"sheenColorFactor\""
"\"sheenColorTexture\""
"\"sheenRoughnessFactor\""
"\"sheenRoughnessTexture\""
"\"KHR_materials_emissive_strength\""
"\"emissiveStrength"\""

View file

@ -0,0 +1,22 @@
/* How to fuzz:
clang main.c -O2 -g -fsanitize=address,fuzzer -o fuzz
cp -r data temp
./fuzz temp/ -dict=gltf.dict -jobs=12 -workers=12
*/
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
cgltf_options options = {cgltf_file_type_invalid};
cgltf_data* data = NULL;
cgltf_result res = cgltf_parse(&options, Data, Size, &data);
if (res == cgltf_result_success)
{
cgltf_validate(data);
cgltf_free(data);
}
return 0;
}

View file

@ -0,0 +1,68 @@
cmake_minimum_required( VERSION 2.8 )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set( EXE_NAME cgltf_test )
add_executable( ${EXE_NAME} main.c )
set_property( TARGET ${EXE_NAME} PROPERTY C_STANDARD 99 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_conversion )
add_executable( ${EXE_NAME} test_conversion.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_write )
add_executable( ${EXE_NAME} test_write.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_math )
add_executable( ${EXE_NAME} test_math.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )
set( EXE_NAME test_strings )
add_executable( ${EXE_NAME} test_strings.cpp )
set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 )
if(MSVC)
target_compile_options(${EXE_NAME} PRIVATE /W4 /WX)
add_definitions( -D_CRT_SECURE_NO_WARNINGS)
else()
target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address)
target_link_options(${EXE_NAME} PUBLIC -fsanitize=address)
endif()
install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin )

View file

@ -0,0 +1,38 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <stdio.h>
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options;
memset(&options, 0, sizeof(cgltf_options));
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data);
if (result == cgltf_result_success)
result = cgltf_load_buffers(&options, data, argv[1]);
if (result == cgltf_result_success)
result = cgltf_validate(data);
printf("Result: %d\n", result);
if (result == cgltf_result_success)
{
printf("Type: %u\n", data->file_type);
printf("Meshes: %u\n", (unsigned)data->meshes_count);
}
cgltf_free(data);
return result;
}

View file

@ -0,0 +1,76 @@
#!/usr/bin/env python
import os, sys
from sys import platform
num_tested = 0
num_errors = 0
def get_executable_path(name):
if platform == "win32":
return "build\\Debug\\" + name
else:
return "build/" + name
def is_ascii(s):
return all(ord(c) < 128 for c in s)
def collect_files(path, type, name):
global num_tested
global num_errors
exe = get_executable_path(name)
for the_file in os.listdir(path):
file_path = os.path.join(os.path.normpath(path), the_file)
if os.path.isfile(file_path):
if the_file.endswith(type):
num_tested = num_tested +1
if is_ascii(file_path):
print("### " + name + " " + file_path)
result = os.system("{0} \"{1}\"".format(exe, file_path))
print("### Result: " + str(result) + "\n")
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
elif os.path.isdir(file_path):
collect_files(file_path, type, name)
if __name__ == "__main__":
if not os.path.exists("build/"):
os.makedirs("build/")
os.chdir("build/")
os.system("cmake ..")
if os.system("cmake --build .") != 0:
print("Unable to build.")
exit(1)
os.chdir("..")
if not os.path.exists("glTF-Sample-Models/"):
os.system("git init glTF-Sample-Models")
os.chdir("glTF-Sample-Models")
os.system("git remote add origin https://github.com/KhronosGroup/glTF-Sample-Models.git")
os.system("git config core.sparsecheckout true")
f = open(".git/info/sparse-checkout", "w+")
f.write("2.0/*\n")
f.close()
os.system("git pull --depth=1 origin master")
os.chdir("..")
collect_files("glTF-Sample-Models/2.0/", ".glb", "cgltf_test")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "cgltf_test")
collect_files("glTF-Sample-Models/2.0/", ".glb", "test_conversion")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_conversion")
collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_write")
result = os.system(get_executable_path("test_math"))
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
result = os.system(get_executable_path("test_strings"))
if result != 0:
num_errors = num_errors + 1
print("Error.")
sys.exit(1)
print("Tested files: " + str(num_tested))
print("Errors: " + str(num_errors))

View file

@ -0,0 +1,92 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <limits>
static bool is_near(cgltf_float a, cgltf_float b)
{
return std::abs(a - b) < 10 * std::numeric_limits<cgltf_float>::min();
}
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options = {};
cgltf_data* data = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data);
if (result == cgltf_result_success)
result = cgltf_load_buffers(&options, data, argv[1]);
if (strstr(argv[1], "Draco"))
{
cgltf_free(data);
return 0;
}
if (result != cgltf_result_success)
return result;
//const cgltf_accessor* blobs = data->accessors;
cgltf_float element_float[16];
cgltf_uint element_uint[4];
for (cgltf_size blob_index = 0; blob_index < data->accessors_count; ++blob_index)
{
const cgltf_accessor* blob = data->accessors + blob_index;
if (blob->is_sparse)
{
cgltf_size nfloats = cgltf_num_components(blob->type) * blob->count;
cgltf_float* dense = (cgltf_float*) malloc(nfloats * sizeof(cgltf_float));
if (cgltf_accessor_unpack_floats(blob, dense, nfloats) < nfloats) {
printf("Unable to completely unpack a sparse accessor.\n");
return -1;
}
free(dense);
continue;
}
if (blob->has_max && blob->has_min)
{
cgltf_float min0 = std::numeric_limits<float>::max();
cgltf_float max0 = std::numeric_limits<float>::lowest();
for (cgltf_size index = 0; index < blob->count; index++)
{
cgltf_accessor_read_float(blob, index, element_float, 16);
min0 = std::min(min0, element_float[0]);
max0 = std::max(max0, element_float[0]);
}
if (!is_near(min0, blob->min[0]) || !is_near(max0, blob->max[0]))
{
printf("Computed [%f, %f] but expected [%f, %f]\n", min0, max0, blob->min[0], blob->max[0]);
return -1;
}
}
if (blob->has_max && blob->has_min && blob->component_type != cgltf_component_type_r_32f && blob->component_type != cgltf_component_type_invalid)
{
cgltf_uint min0 = std::numeric_limits<unsigned int>::max();
cgltf_uint max0 = std::numeric_limits<unsigned int>::lowest();
for ( cgltf_size index = 0; index < blob->count; index++ )
{
cgltf_accessor_read_uint( blob, index, element_uint, 4 );
min0 = std::min( min0, element_uint[0] );
max0 = std::max( max0, element_uint[0] );
}
if ( min0 != (unsigned int) blob->min[0] || max0 != (unsigned int) blob->max[0] )
{
printf( "Computed [%u, %u] but expected [%u, %u]\n", min0, max0, (unsigned int) blob->min[0], (unsigned int) blob->max[0] );
return -1;
}
}
}
cgltf_free(data);
return result;
}

View file

@ -0,0 +1,52 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
// Performs matrix-vector multiplication, as in (4x4) * (4x1) = (4x1)
static void transform(const cgltf_float matrix[16], const cgltf_float source[4], cgltf_float target[4]) {
target[0] = matrix[0] * source[0] + matrix[4] * source[1] + matrix[ 8] * source[2] + matrix[12] * source[3];
target[1] = matrix[1] * source[0] + matrix[5] * source[1] + matrix[ 9] * source[2] + matrix[13] * source[3];
target[2] = matrix[2] * source[0] + matrix[6] * source[1] + matrix[10] * source[2] + matrix[14] * source[3];
target[3] = matrix[3] * source[0] + matrix[7] * source[1] + matrix[11] * source[2] + matrix[15] * source[3];
}
static void set(cgltf_float target[3], float x, float y, float z) {
target[0] = x;
target[1] = y;
target[2] = z;
}
static void check(cgltf_float target[3], float x, float y, float z) {
if (target[0] != x || target[1] != y || target[2] != z) {
fprintf(stderr, "Mismatch detected.\n");
exit(1);
}
}
int main(int, char**)
{
cgltf_node node = {};
cgltf_float matrix[16];
cgltf_float source[4] = {1, 2, 3, 1};
cgltf_float target[4];
set(node.scale, 1, 1, 1);
set(node.translation, 1, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 2, 2, 3);
set(node.scale, 3, 1, 1);
set(node.translation, 0, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 3, 2, 3);
set(node.scale, 1, 3, 1);
set(node.translation, 1, 0, 0);
cgltf_node_transform_local(&node, matrix);
transform(matrix, source, target);
check(target, 2, 6, 3);
return 0;
}

View file

@ -0,0 +1,57 @@
#define CGLTF_IMPLEMENTATION
#include "../cgltf.h"
#include <cstring>
static void check(const char* a, const char* b, cgltf_size size) {
if (strcmp(a, b) != 0 || strlen(a) != size) {
fprintf(stderr, "Mismatch detected.\n");
exit(1);
}
}
int main(int, char**)
{
char string[64];
cgltf_size size;
// cgltf_decode_string
strcpy(string, "");
size = cgltf_decode_string(string);
check(string, "", size);
strcpy(string, "nothing to replace");
size = cgltf_decode_string(string);
check(string, "nothing to replace", size);
strcpy(string, "\\\" \\/ \\\\ \\b \\f \\r \\n \\t \\u0030");
size = cgltf_decode_string(string);
check(string, "\" / \\ \b \f \r \n \t 0", size);
strcpy(string, "test \\u121b\\u130d\\u1294\\u1276\\u127d test");
size = cgltf_decode_string(string);
check(string, "test ማግኔቶች test", size);
// cgltf_decode_uri
strcpy(string, "");
size = cgltf_decode_uri(string);
check(string, "", size);
strcpy(string, "nothing to replace");
size = cgltf_decode_uri(string);
check(string, "nothing to replace", size);
strcpy(string, "%2F%D0%BA%D0%B8%D1%80%D0%B8%D0%BB%D0%BB%D0%B8%D1%86%D0%B0");
size = cgltf_decode_uri(string);
check(string, "/кириллица", size);
strcpy(string, "test%20%E1%88%9B%E1%8C%8D%E1%8A%94%E1%89%B6%E1%89%BD%20test");
size = cgltf_decode_uri(string);
check(string, "test ማግኔቶች test", size);
strcpy(string, "%%2F%X%AX%%2F%%");
size = cgltf_decode_uri(string);
check(string, "%/%X%AX%/%%", size);
return 0;
}

View file

@ -0,0 +1,45 @@
#define CGLTF_IMPLEMENTATION
#define CGLTF_WRITE_IMPLEMENTATION
#include "../cgltf_write.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <limits>
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("err\n");
return -1;
}
cgltf_options options = {};
cgltf_data* data0 = NULL;
cgltf_result result = cgltf_parse_file(&options, argv[1], &data0);
// Silently skip over files that are unreadable since this is a writing test.
if (result != cgltf_result_success)
{
return cgltf_result_success;
}
result = cgltf_write_file(&options, "out.gltf", data0);
if (result != cgltf_result_success)
{
return result;
}
cgltf_data* data1 = NULL;
result = cgltf_parse_file(&options, "out.gltf", &data1);
if (result != cgltf_result_success)
{
return result;
}
if (data0->meshes_count != data1->meshes_count) {
return -1;
}
cgltf_free(data1);
cgltf_free(data0);
return cgltf_result_success;
}

@ -1 +0,0 @@
Subproject commit da35f9d6c7374a95353fd1df1d394d44ab66cf01

View file

@ -0,0 +1 @@
github: mackron

View file

@ -0,0 +1,12 @@
docs
tests/testvectors/opus/*
!tests/testvectors/opus/DO_NOT_DELETE.md
tests/bin/*
!tests/bin/DO_NOT_DELETE.md
tests/build
tests/flac/include
tests/flac/lib
tests/testvectors/flac/tests/*
!tests/testvectors/flac/tests/DO_NOT_DELETE.md
todo
_private

View file

@ -0,0 +1,3 @@
[submodule "tests/external/miniaudio"]
path = tests/external/miniaudio
url = https://github.com/dr-soft/miniaudio.git

View file

@ -0,0 +1,47 @@
This software is available as a choice of the following licenses. Choose
whichever you prefer.
===============================================================================
ALTERNATIVE 1 - 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.
For more information, please refer to <http://unlicense.org/>
===============================================================================
ALTERNATIVE 2 - MIT No Attribution
===============================================================================
Copyright 2020 David Reid
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.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,21 @@
<h4 align="center">Public domain, single file audio decoding libraries for C and C++.</h4>
<p align="center">
<a href="https://discord.gg/9vpqbjU"><img src="https://img.shields.io/discord/712952679415939085?label=discord&logo=discord&style=flat-square" alt="discord"></a>
<a href="https://fosstodon.org/@mackron"><img src="https://img.shields.io/mastodon/follow/109293691403797709?color=blue&domain=https%3A%2F%2Ffosstodon.org&label=mastodon&logo=mastodon&style=flat-square" alt="mastodon"></a>
</p>
Library | Description
----------------------------------------------- | -----------
[dr_flac](dr_flac.h) | FLAC audio decoder.
[dr_mp3](dr_mp3.h) | MP3 audio decoder. Based off [minimp3](https://github.com/lieff/minimp3).
[dr_wav](dr_wav.h) | WAV audio loader and writer.
# Other Libraries
Below are some of my other libraries you may be interested in.
Library | Description
------------------------------------------------- | -----------
[miniaudio](https://github.com/mackron/miniaudio) | A public domain, single file library for audio playback and recording.

12536
source/engine/thirdparty/dr_libs/dr_flac.h vendored Normal file

File diff suppressed because it is too large Load diff

4837
source/engine/thirdparty/dr_libs/dr_mp3.h vendored Normal file

File diff suppressed because it is too large Load diff

8815
source/engine/thirdparty/dr_libs/dr_wav.h vendored Normal file

File diff suppressed because it is too large Load diff

4755
source/engine/thirdparty/dr_libs/old/dr.h vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,676 @@
// Public Domain. See "unlicense" statement at the end of this file.
// NOTE: This is still very much work in progress and is only being updated as I need it. You don't want to be using this library
// in its current state.
// QUICK NOTES
// - This library does not use SSE for its basic types (vec4, etc.). Rationale: 1) It keeps things simple; 2) SSE is not always
// faster than the FPU(s) on modern CPUs; 3) The library can always implement functions that work on __m128 variables directly
// in the future if the need arises; 4) It doesn't work well with the pass-by-value API this library uses.
// - Use DISABLE_SSE to disable SSE optimized functions.
// - Angles are always specified in radians, unless otherwise noted. Rationale: Consistency with the standard library and most
// other math libraries.
// - Use radians() and degrees() to convert between the two.
#ifndef dr_math_h
#define dr_math_h
#include <math.h>
#if defined(_MSC_VER)
#define DR_MATHCALL static __forceinline
#else
#define DR_MATHCALL static inline
#endif
#define DR_PI 3.14159265358979323846
#define DR_PIF 3.14159265358979323846f
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
float x;
float y;
float z;
float w;
} vec4;
typedef struct
{
float x;
float y;
float z;
} vec3;
typedef struct
{
float x;
float y;
} vec2;
typedef struct
{
vec4 col[4];
} mat4;
typedef struct
{
float x;
float y;
float z;
float w;
} quat;
// Radians to degrees.
DR_MATHCALL float dr_degrees(float radians)
{
return radians * 57.29577951308232087685f;
}
// Degrees to radians.
DR_MATHCALL float dr_radians(float degrees)
{
return degrees * 0.01745329251994329577f;
}
///////////////////////////////////////////////
//
// VEC4
//
///////////////////////////////////////////////
DR_MATHCALL vec4 vec4f(float x, float y, float z, float w)
{
vec4 result;
result.x = x;
result.y = y;
result.z = z;
result.w = w;
return result;
}
DR_MATHCALL vec4 vec4v(const float* v)
{
return vec4f(v[0], v[1], v[2], v[3]);
}
DR_MATHCALL vec4 vec4_zero()
{
return vec4f(0, 0, 0, 0);
}
DR_MATHCALL vec4 vec4_one()
{
return vec4f(1, 1, 1, 1);
}
DR_MATHCALL vec4 vec4_add(vec4 a, vec4 b)
{
return vec4f(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}
DR_MATHCALL vec4 vec4_sub(vec4 a, vec4 b)
{
return vec4f(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
}
DR_MATHCALL vec4 vec4_mul(vec4 a, vec4 b)
{
return vec4f(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
}
DR_MATHCALL vec4 vec4_mul_1f(vec4 a, float x)
{
return vec4f(a.x * x, a.y * x, a.z * x, a.w * x);
}
DR_MATHCALL vec4 vec4_mul_mat4(vec4 v, mat4 m)
{
const vec4 m0 = m.col[0];
const vec4 m1 = m.col[1];
const vec4 m2 = m.col[2];
const vec4 m3 = m.col[3];
return vec4f(
m0.x*v.x + m0.y*v.y + m0.z*v.z + m0.w*v.w,
m1.x*v.x + m1.y*v.y + m1.z*v.z + m1.w*v.w,
m2.x*v.x + m2.y*v.y + m2.z*v.z + m2.w*v.w,
m3.x*v.x + m3.y*v.y + m3.z*v.z + m3.w*v.w
);
}
DR_MATHCALL vec4 vec4_div(vec4 a, vec4 b)
{
return vec4f(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
}
///////////////////////////////////////////////
//
// VEC3
//
///////////////////////////////////////////////
DR_MATHCALL vec3 vec3f(float x, float y, float z)
{
vec3 result;
result.x = x;
result.y = y;
result.z = z;
return result;
}
DR_MATHCALL vec3 vec3v(const float* v)
{
return vec3f(v[0], v[1], v[2]);
}
DR_MATHCALL vec3 vec3_zero()
{
return vec3f(0, 0, 0);
}
DR_MATHCALL vec3 vec3_one()
{
return vec3f(1, 1, 1);
}
DR_MATHCALL vec3 vec3_add(vec3 a, vec3 b)
{
return vec3f(a.x + b.x, a.y + b.y, a.z + b.z);
}
DR_MATHCALL vec3 vec3_sub(vec3 a, vec3 b)
{
return vec3f(a.x - b.x, a.y - b.y, a.z - b.z);
}
DR_MATHCALL vec3 vec3_mul(vec3 a, vec3 b)
{
return vec3f(a.x * b.x, a.y * b.y, a.z * b.z);
}
DR_MATHCALL vec3 vec3_mul_1f(vec3 a, float x)
{
return vec3f(a.x * x, a.y * x, a.z * x);
}
DR_MATHCALL vec3 vec3_div(vec3 a, vec3 b)
{
return vec3f(a.x / b.x, a.y / b.y, a.z / b.z);
}
DR_MATHCALL float vec3_dot(vec3 a, vec3 b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
DR_MATHCALL float vec3_length2(vec3 a)
{
return vec3_dot(a, a);
}
DR_MATHCALL float vec3_length(vec3 a)
{
return sqrtf(vec3_length2(a));
}
DR_MATHCALL vec3 vec3_normalize(vec3 a)
{
float len = vec3_length(a);
return vec3f(
a.x / len,
a.y / len,
a.z / len
);
}
DR_MATHCALL vec3 vec3_cross(vec3 a, vec3 b)
{
return vec3f(
a.y*b.z - a.z*b.y,
a.z*b.x - a.x*b.z,
a.x*b.y - a.y*b.x
);
}
DR_MATHCALL vec3 vec3_triangle_normal(vec3 p1, vec3 p2, vec3 p3)
{
vec3 u = vec3_sub(p2, p1);
vec3 v = vec3_sub(p3, p1);
return vec3_normalize(vec3_cross(u, v));
}
///////////////////////////////////////////////
//
// VEC2
//
///////////////////////////////////////////////
DR_MATHCALL vec2 vec2f(float x, float y)
{
vec2 result;
result.x = x;
result.y = y;
return result;
}
DR_MATHCALL vec2 vec2v(const float* v)
{
return vec2f(v[0], v[1]);
}
DR_MATHCALL vec2 vec2_zero()
{
return vec2f(0, 0);
}
DR_MATHCALL vec2 vec2_one()
{
return vec2f(1, 1);
}
DR_MATHCALL vec2 vec2_add(vec2 a, vec2 b)
{
return vec2f(a.x + b.x, a.y + b.y);
}
DR_MATHCALL vec2 vec2_sub(vec2 a, vec2 b)
{
return vec2f(a.x - b.x, a.y - b.y);
}
DR_MATHCALL vec2 vec2_mul(vec2 a, vec2 b)
{
return vec2f(a.x * b.x, a.y * b.y);
}
DR_MATHCALL vec2 vec2_mul_1f(vec2 a, float x)
{
return vec2f(a.x * x, a.y * x);
}
DR_MATHCALL vec2 vec2_div(vec2 a, vec2 b)
{
return vec2f(a.x / b.x, a.y / b.y);
}
DR_MATHCALL float vec2_dot(vec2 a, vec2 b)
{
return a.x*b.x + a.y*b.y;
}
DR_MATHCALL float vec2_length2(vec2 a)
{
return vec2_dot(a, a);
}
DR_MATHCALL float vec2_length(vec2 a)
{
return sqrtf(vec2_length2(a));
}
DR_MATHCALL vec2 vec2_normalize(vec2 a)
{
float len = vec2_length(a);
return vec2f(
a.x / len,
a.y / len
);
}
DR_MATHCALL float vec2_angle(vec2 a, vec2 b)
{
return atanf(a.y / a.x) - atanf(b.y / b.x);
}
DR_MATHCALL vec2 vec2_rotate(vec2 a, float angleInRadians)
{
float c = cosf(angleInRadians);
float s = sinf(angleInRadians);
return vec2f(
a.x*c - a.y*s,
a.x*s + a.y*c
);
}
///////////////////////////////////////////////
//
// MAT4
//
///////////////////////////////////////////////
DR_MATHCALL mat4 mat4f(vec4 col0, vec4 col1, vec4 col2, vec4 col3)
{
mat4 result;
result.col[0] = col0;
result.col[1] = col1;
result.col[2] = col2;
result.col[3] = col3;
return result;
}
DR_MATHCALL mat4 mat4_identity()
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, 1, 0, 0);
result.col[2] = vec4f(0, 0, 1, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_ortho(float left, float right, float bottom, float top, float znear, float zfar)
{
float rml = right - left;
float tmb = top - bottom;
float fmn = zfar - znear;
float rpl = right + left;
float tpb = top + bottom;
float fpn = zfar + znear;
mat4 result;
result.col[0] = vec4f(2/rml, 0, 0, 0);
result.col[1] = vec4f(0, 2/tmb, 0, 0);
result.col[2] = vec4f(0, 0, -2/fmn, 0);
result.col[3] = vec4f(-(rpl/rml), -(tpb/tmb), -(fpn/fmn), 1);
return result;
}
DR_MATHCALL mat4 mat4_perspective(float fovy, float aspect, float znear, float zfar)
{
float f = (float)tan(DR_PI/2 - fovy/2);
mat4 result;
result.col[0] = vec4f(f / aspect, 0, 0, 0);
result.col[1] = vec4f(0, f, 0, 0);
result.col[2] = vec4f(0, 0, (zfar + znear) / (znear - zfar), -1);
result.col[3] = vec4f(0, 0, (2 * zfar * znear) / (znear - zfar), 0);
return result;
}
DR_MATHCALL mat4 mat4_vulkan_clip_correction()
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, -1, 0, 0);
result.col[2] = vec4f(0, 0, 0.5f, 0);
result.col[3] = vec4f(0, 0, 0.5f, 1);
return result;
}
DR_MATHCALL mat4 mat4_translate(vec3 translation)
{
mat4 result;
result.col[0] = vec4f(1, 0, 0, 0);
result.col[1] = vec4f(0, 1, 0, 0);
result.col[2] = vec4f(0, 0, 1, 0);
result.col[3] = vec4f(translation.x, translation.y, translation.z, 1);
return result;
}
DR_MATHCALL mat4 mat4_rotate(float angleInRadians, vec3 axis)
{
float c = cosf(angleInRadians);
float s = sinf(angleInRadians);
float x = axis.x;
float y = axis.y;
float z = axis.z;
float xx = x*x;
float xy = x*y;
float xz = x*z;
float yy = y*y;
float yz = y*z;
float zz = z*z;
float xs = x*s;
float ys = y*s;
float zs = z*s;
mat4 result;
result.col[0] = vec4f(xx * (1 - c) + c, xy * (1 - c) - zs, xz * (1 - c) + ys, 0);
result.col[1] = vec4f(xy * (1 - c) + zs, yy * (1 - c) + c, yz * (1 - c) - xs, 0);
result.col[2] = vec4f(xz * (1 - c) - ys, yz * (1 - c) + xs, zz * (1 - c) + c, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_scale(vec3 scale)
{
mat4 result;
result.col[0] = vec4f(scale.x, 0, 0, 0);
result.col[1] = vec4f(0, scale.y, 0, 0);
result.col[2] = vec4f(0, 0, scale.z, 0);
result.col[3] = vec4f(0, 0, 0, 1);
return result;
}
DR_MATHCALL mat4 mat4_mul(mat4 a, mat4 b)
{
const vec4 a0 = a.col[0];
const vec4 a1 = a.col[1];
const vec4 a2 = a.col[2];
const vec4 a3 = a.col[3];
const vec4 b0 = b.col[0];
const vec4 b1 = b.col[1];
const vec4 b2 = b.col[2];
const vec4 b3 = b.col[3];
mat4 result;
result.col[0] = vec4f(
a0.x*b0.x + a1.x*b0.y + a2.x*b0.z + a3.x*b0.w,
a0.y*b0.x + a1.y*b0.y + a2.y*b0.z + a3.y*b0.w,
a0.z*b0.x + a1.z*b0.y + a2.z*b0.z + a3.z*b0.w,
a0.w*b0.x + a1.w*b0.y + a2.w*b0.z + a3.w*b0.w
);
result.col[1] = vec4f(
a0.x*b1.x + a1.x*b1.y + a2.x*b1.z + a3.x*b1.w,
a0.y*b1.x + a1.y*b1.y + a2.y*b1.z + a3.y*b1.w,
a0.z*b1.x + a1.z*b1.y + a2.z*b1.z + a3.z*b1.w,
a0.w*b1.x + a1.w*b1.y + a2.w*b1.z + a3.w*b1.w
);
result.col[2] = vec4f(
a0.x*b2.x + a1.x*b2.y + a2.x*b2.z + a3.x*b2.w,
a0.y*b2.x + a1.y*b2.y + a2.y*b2.z + a3.y*b2.w,
a0.z*b2.x + a1.z*b2.y + a2.z*b2.z + a3.z*b2.w,
a0.w*b2.x + a1.w*b2.y + a2.w*b2.z + a3.w*b2.w
);
result.col[3] = vec4f(
a0.x*b3.x + a1.x*b3.y + a2.x*b3.z + a3.x*b3.w,
a0.y*b3.x + a1.y*b3.y + a2.y*b3.z + a3.y*b3.w,
a0.z*b3.x + a1.z*b3.y + a2.z*b3.z + a3.z*b3.w,
a0.w*b3.x + a1.w*b3.y + a2.w*b3.z + a3.w*b3.w
);
return result;
}
DR_MATHCALL vec4 mat4_mul_vec4(mat4 m, vec4 v)
{
const vec4 m0 = m.col[0];
const vec4 m1 = m.col[1];
const vec4 m2 = m.col[2];
const vec4 m3 = m.col[3];
return vec4f(
m0.x*v.x + m1.x*v.y + m2.x*v.z + m3.x*v.w,
m0.y*v.x + m1.y*v.y + m2.y*v.z + m3.y*v.w,
m0.z*v.x + m1.z*v.y + m2.z*v.z + m3.z*v.w,
m0.w*v.x + m1.w*v.y + m2.w*v.z + m3.w*v.w
);
}
///////////////////////////////////////////////
//
// QUAT
//
///////////////////////////////////////////////
DR_MATHCALL quat quatf(float x, float y, float z, float w)
{
quat result;
result.x = x;
result.y = y;
result.z = z;
result.w = w;
return result;
}
DR_MATHCALL quat quatv(const float* v)
{
return quatf(v[0], v[1], v[2], v[3]);
}
DR_MATHCALL quat quat_identity()
{
return quatf(0, 0, 0, 1);
}
///////////////////////////////////////////////
//
// TRANSFORM
//
///////////////////////////////////////////////
typedef struct
{
vec3 position;
quat rotation;
vec3 scale;
}transform_t;
DR_MATHCALL transform_t transform_init(vec3 position, quat rotation, vec3 scale)
{
transform_t result;
result.position = position;
result.rotation = rotation;
result.scale = scale;
return result;
}
DR_MATHCALL transform_t transform_identity()
{
transform_t result;
result.position = vec3_zero();
result.rotation = quat_identity();
result.scale = vec3_one();
return result;
}
DR_MATHCALL transform_t transform_translate(transform_t transform, vec3 offset)
{
transform_t result = transform;
result.position = vec3_add(transform.position, offset);
return result;
}
///////////////////////////////////////////////
//
// SSE IMPLEMENTATION
//
///////////////////////////////////////////////
// Not supporting SSE on x86/MSVC due to pass-by-value errors with aligned types.
#if (defined(_MSC_VER) && defined(_M_X64)) || defined(__SSE2__)
#define SUPPORTS_SSE
#endif
#if !defined(DISABLE_SSE) && defined(SUPPORTS_SSE)
#define ENABLE_SSE
#endif
#ifdef ENABLE_SSE
#if defined(__MINGW32__)
#include <intrin.h>
#endif
#include <emmintrin.h>
#endif
#ifdef __cplusplus
}
#endif
#endif //dr_math_h
/*
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 <http://unlicense.org/>
*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,793 @@
// PCX image loader. Public domain. See "unlicense" statement at the end of this file.
// dr_pcx - v0.3.1 - 2018-09-11
//
// David Reid - mackron@gmail.com
// USAGE
//
// dr_pcx is a single-file library. To use it, do something like the following in one .c file.
// #define DR_PCX_IMPLEMENTATION
// #include "dr_pcx.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 load and decode an image:
//
// int width;
// int height;
// int components
// drpcx_uint8* pImageData = drpcx_load_file("my_image.pcx", DRPCX_FALSE, &width, &height, &components, 0);
// if (pImageData == NULL) {
// // Failed to load image.
// }
//
// ...
//
// drpcx_free(pImageData);
//
// The boolean parameter (second argument in the above example) is whether or not the image should be flipped upside down.
//
//
//
// OPTIONS
// #define these options before including this file.
//
// #define DR_PCX_NO_STDIO
// Disable drpcx_load_file().
//
//
//
// QUICK NOTES
// - 2-bpp/4-plane and 4-bpp/1-plane formats have not been tested.
#ifndef dr_pcx_h
#define dr_pcx_h
#include <stddef.h>
#if defined(_MSC_VER) && _MSC_VER < 1600
typedef signed char drpcx_int8;
typedef unsigned char drpcx_uint8;
typedef signed short drpcx_int16;
typedef unsigned short drpcx_uint16;
typedef signed int drpcx_int32;
typedef unsigned int drpcx_uint32;
typedef signed __int64 drpcx_int64;
typedef unsigned __int64 drpcx_uint64;
#else
#include <stdint.h>
typedef int8_t drpcx_int8;
typedef uint8_t drpcx_uint8;
typedef int16_t drpcx_int16;
typedef uint16_t drpcx_uint16;
typedef int32_t drpcx_int32;
typedef uint32_t drpcx_uint32;
typedef int64_t drpcx_int64;
typedef uint64_t drpcx_uint64;
#endif
typedef drpcx_uint8 drpcx_bool8;
typedef drpcx_uint32 drpcx_bool32;
#define DRPCX_TRUE 1
#define DRPCX_FALSE 0
#ifdef __cplusplus
extern "C" {
#endif
// Callback for when data is read. Return value is the number of bytes actually read.
typedef size_t (* drpcx_read_proc)(void* userData, void* bufferOut, size_t bytesToRead);
// Loads a PCX file using the given callbacks.
drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
// Frees memory returned by drpcx_load() and family.
void drpcx_free(void* pReturnValueFromLoad);
#ifndef DR_PCX_NO_STDIO
// Loads an PCX file from an actual file.
drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
#endif
// Helper for loading an PCX file from a block of memory.
drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents);
#ifdef __cplusplus
}
#endif
#endif // dr_pcx_h
///////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION
//
///////////////////////////////////////////////////////////////////////////////
#ifdef DR_PCX_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifndef DR_PCX_NO_STDIO
#include <stdio.h>
static size_t drpcx__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
{
return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
}
drpcx_uint8* drpcx_load_file(const char* filename, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
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
drpcx_uint8* pImageData = drpcx_load(drpcx__on_read_stdio, pFile, flipped, x, y, internalComponents, desiredComponents);
fclose(pFile);
return pImageData;
}
#endif // DR_PCX_NO_STDIO
typedef struct
{
// A pointer to the beginning of the data. We use a char as the type here for easy offsetting.
const unsigned char* data;
size_t dataSize;
size_t currentReadPos;
} drpcx_memory;
static size_t drpcx__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
{
drpcx_memory* memory = (drpcx_memory*)pUserData;
assert(memory != NULL);
assert(memory->dataSize >= memory->currentReadPos);
size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining;
}
if (bytesToRead > 0) {
memcpy(bufferOut, memory->data + memory->currentReadPos, bytesToRead);
memory->currentReadPos += bytesToRead;
}
return bytesToRead;
}
drpcx_uint8* drpcx_load_memory(const void* data, size_t dataSize, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
drpcx_memory memory;
memory.data = (const unsigned char*)data;
memory.dataSize = dataSize;
memory.currentReadPos = 0;
return drpcx_load(drpcx__on_read_memory, &memory, flipped, x, y, internalComponents, desiredComponents);
}
typedef struct
{
drpcx_uint8 header;
drpcx_uint8 version;
drpcx_uint8 encoding;
drpcx_uint8 bpp;
drpcx_uint16 left;
drpcx_uint16 top;
drpcx_uint16 right;
drpcx_uint16 bottom;
drpcx_uint16 hres;
drpcx_uint16 vres;
drpcx_uint8 palette16[48];
drpcx_uint8 reserved1;
drpcx_uint8 bitPlanes;
drpcx_uint16 bytesPerLine;
drpcx_uint16 paletteType;
drpcx_uint16 screenSizeH;
drpcx_uint16 screenSizeV;
drpcx_uint8 reserved2[54];
} drpcx_header;
typedef struct
{
drpcx_read_proc onRead;
void* pUserData;
drpcx_bool32 flipped;
drpcx_header header;
drpcx_uint32 width;
drpcx_uint32 height;
drpcx_uint32 components; // 3 = RGB; 4 = RGBA. Only 3 and 4 are supported.
drpcx_uint8* pImageData;
} drpcx;
static drpcx_uint8 drpcx__read_byte(drpcx* pPCX)
{
drpcx_uint8 byte = 0;
pPCX->onRead(pPCX->pUserData, &byte, 1);
return byte;
}
static drpcx_uint8* drpcx__row_ptr(drpcx* pPCX, drpcx_uint32 row)
{
drpcx_uint32 stride = pPCX->width * pPCX->components;
drpcx_uint8* pRow = pPCX->pImageData;
if (pPCX->flipped) {
pRow += (pPCX->height - row - 1) * stride;
} else {
pRow += row * stride;
}
return pRow;
}
static drpcx_uint8 drpcx__rle(drpcx* pPCX, drpcx_uint8* pRLEValueOut)
{
drpcx_uint8 rleCount;
drpcx_uint8 rleValue;
rleValue = drpcx__read_byte(pPCX);
if ((rleValue & 0xC0) == 0xC0) {
rleCount = rleValue & 0x3F;
rleValue = drpcx__read_byte(pPCX);
} else {
rleCount = 1;
}
*pRLEValueOut = rleValue;
return rleCount;
}
drpcx_bool32 drpcx__decode_1bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
switch (pPCX->header.bitPlanes)
{
case 1:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
drpcx_uint8 mask = (1 << (7 - bit));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
pRow[0] = paletteIndex * 255;
pRow[1] = paletteIndex * 255;
pRow[2] = paletteIndex * 255;
pRow += 3;
}
}
}
return DRPCX_TRUE;
} break;
case 2:
case 3:
case 4:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; (bit < 8) && ((x*8 + bit) < pPCX->width); ++bit) {
drpcx_uint8 mask = (1 << (7 - bit));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (7 - bit);
pRow[0] |= ((paletteIndex & 0x01) << c);
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
}
default: return DRPCX_FALSE;
}
}
drpcx_bool32 drpcx__decode_2bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
switch (pPCX->header.bitPlanes)
{
case 1:
{
drpcx_uint8 paletteCGA[48];
paletteCGA[ 0] = 0x00; paletteCGA[ 1] = 0x00; paletteCGA[ 2] = 0x00; // #000000
paletteCGA[ 3] = 0x00; paletteCGA[ 4] = 0x00; paletteCGA[ 5] = 0xAA; // #0000AA
paletteCGA[ 6] = 0x00; paletteCGA[ 7] = 0xAA; paletteCGA[ 8] = 0x00; // #00AA00
paletteCGA[ 9] = 0x00; paletteCGA[10] = 0xAA; paletteCGA[11] = 0xAA; // #00AAAA
paletteCGA[12] = 0xAA; paletteCGA[13] = 0x00; paletteCGA[14] = 0x00; // #AA0000
paletteCGA[15] = 0xAA; paletteCGA[16] = 0x00; paletteCGA[17] = 0xAA; // #AA00AA
paletteCGA[18] = 0xAA; paletteCGA[19] = 0x55; paletteCGA[20] = 0x00; // #AA5500
paletteCGA[21] = 0xAA; paletteCGA[22] = 0xAA; paletteCGA[23] = 0xAA; // #AAAAAA
paletteCGA[24] = 0x55; paletteCGA[25] = 0x55; paletteCGA[26] = 0x55; // #555555
paletteCGA[27] = 0x55; paletteCGA[28] = 0x55; paletteCGA[29] = 0xFF; // #5555FF
paletteCGA[30] = 0x55; paletteCGA[31] = 0xFF; paletteCGA[32] = 0x55; // #55FF55
paletteCGA[33] = 0x55; paletteCGA[34] = 0xFF; paletteCGA[35] = 0xFF; // #55FFFF
paletteCGA[36] = 0xFF; paletteCGA[37] = 0x55; paletteCGA[38] = 0x55; // #FF5555
paletteCGA[39] = 0xFF; paletteCGA[40] = 0x55; paletteCGA[41] = 0xFF; // #FF55FF
paletteCGA[42] = 0xFF; paletteCGA[43] = 0xFF; paletteCGA[44] = 0x55; // #FFFF55
paletteCGA[45] = 0xFF; paletteCGA[46] = 0xFF; paletteCGA[47] = 0xFF; // #FFFFFF
drpcx_uint8 cgaBGColor = pPCX->header.palette16[0] >> 4;
drpcx_uint8 i = (pPCX->header.palette16[3] & 0x20) >> 5;
drpcx_uint8 p = (pPCX->header.palette16[3] & 0x40) >> 6;
//drpcx_uint8 c = (pPCX->header.palette16[3] & 0x80) >> 7; // Color or monochrome. How is monochrome handled?
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bit = 0; bit < 4; ++bit) {
if (x*4 + bit < pPCX->width) {
drpcx_uint8 mask = (3 << ((3 - bit) * 2));
drpcx_uint8 paletteIndex = (rleValue & mask) >> ((3 - bit) * 2);
drpcx_uint8 cgaIndex;
if (paletteIndex == 0) { // Background.
cgaIndex = cgaBGColor;
} else { // Foreground
cgaIndex = (((paletteIndex << 1) + p) + (i << 3));
}
pRow[0] = paletteCGA[cgaIndex*3 + 0];
pRow[1] = paletteCGA[cgaIndex*3 + 1];
pRow[2] = paletteCGA[cgaIndex*3 + 2];
pRow += 3;
}
}
}
}
// TODO: According to http://www.fysnet.net/pcxfile.htm, we should use the palette at the end of the file
// instead of the standard CGA palette if the version is equal to 5. With my test files the palette
// at the end of the file does not exist. Research this one.
if (pPCX->header.version == 5) {
drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
if (paletteMarker == 0x0C) {
// TODO: Implement Me.
}
}
return DRPCX_TRUE;
};
case 4:
{
// NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
// TODO: Test Me.
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int bitpair = 0; (bitpair < 4) && ((x*4 + bitpair) < pPCX->width); ++bitpair) {
drpcx_uint8 mask = (4 << (3 - bitpair));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (3 - bitpair);
pRow[0] |= ((paletteIndex & 0x03) << (c*2));
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
};
default: return DRPCX_FALSE;
}
}
drpcx_bool32 drpcx__decode_4bit(drpcx* pPCX)
{
// NOTE: This is completely untested. If anybody knows where I can get a test file please let me know or send it through to me!
// TODO: Test Me.
if (pPCX->header.bitPlanes > 1) {
return DRPCX_FALSE;
}
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->header.bitPlanes; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
for (int nibble = 0; (nibble < 2) && ((x*2 + nibble) < pPCX->width); ++nibble)
{
drpcx_uint8 mask = (4 << (1 - nibble));
drpcx_uint8 paletteIndex = (rleValue & mask) >> (1 - nibble);
pRow[0] |= ((paletteIndex & 0x0F) << (c*4));
pRow += pPCX->components;
}
}
}
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 paletteIndex = pRow[0];
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
pRow[c] = pPCX->header.palette16[paletteIndex*3 + c];
}
pRow += pPCX->components;
}
}
return DRPCX_TRUE;
}
drpcx_bool32 drpcx__decode_8bit(drpcx* pPCX)
{
drpcx_uint8 rleCount = 0;
drpcx_uint8 rleValue = 0;
drpcx_uint32 stride = pPCX->width * pPCX->components;
switch (pPCX->header.bitPlanes)
{
case 1:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
if (x < pPCX->width) {
pRow[0] = rleValue;
pRow[1] = rleValue;
pRow[2] = rleValue;
pRow += 3;
}
}
}
// At this point we can know if we are dealing with a palette or a grayscale image by checking the next byte. If it's equal to 0x0C, we
// need to do a simple palette lookup.
drpcx_uint8 paletteMarker = drpcx__read_byte(pPCX);
if (paletteMarker == 0x0C) {
// A palette is present - we need to do a second pass.
drpcx_uint8 palette256[768];
if (pPCX->onRead(pPCX->pUserData, palette256, sizeof(palette256)) != sizeof(palette256)) {
return DRPCX_FALSE;
}
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
drpcx_uint8* pRow = pPCX->pImageData + (y * stride);
for (drpcx_uint32 x = 0; x < pPCX->width; ++x) {
drpcx_uint8 index = pRow[0];
pRow[0] = palette256[index*3 + 0];
pRow[1] = palette256[index*3 + 1];
pRow[2] = palette256[index*3 + 2];
pRow += 3;
}
}
}
return DRPCX_TRUE;
}
case 3:
case 4:
{
for (drpcx_uint32 y = 0; y < pPCX->height; ++y) {
for (drpcx_uint32 c = 0; c < pPCX->components; ++c) {
drpcx_uint8* pRow = drpcx__row_ptr(pPCX, y);
for (drpcx_uint32 x = 0; x < pPCX->header.bytesPerLine; ++x) {
if (rleCount == 0) {
rleCount = drpcx__rle(pPCX, &rleValue);
}
rleCount -= 1;
if (x < pPCX->width) {
pRow[c] = rleValue;
pRow += pPCX->components;
}
}
}
}
return DRPCX_TRUE;
}
}
return DRPCX_TRUE;
}
drpcx_uint8* drpcx_load(drpcx_read_proc onRead, void* pUserData, drpcx_bool32 flipped, int* x, int* y, int* internalComponents, int desiredComponents)
{
if (onRead == NULL) return NULL;
if (desiredComponents > 4) return NULL;
drpcx pcx;
pcx.onRead = onRead;
pcx.pUserData = pUserData;
pcx.flipped = flipped;
if (onRead(pUserData, &pcx.header, sizeof(pcx.header)) != sizeof(pcx.header)) {
return NULL; // Failed to read the header.
}
if (pcx.header.header != 10) {
return NULL; // Not a PCX file.
}
if (pcx.header.encoding != 1) {
return NULL; // Not supporting non-RLE encoding. Would assume a value of 0 indicates raw, unencoded, but that is apparently never used.
}
if (pcx.header.bpp != 1 && pcx.header.bpp != 2 && pcx.header.bpp != 4 && pcx.header.bpp != 8) {
return NULL; // Unsupported pixel format.
}
if (pcx.header.left > pcx.header.right) {
drpcx_uint16 temp = pcx.header.left;
pcx.header.left = pcx.header.right;
pcx.header.right = temp;
}
if (pcx.header.top > pcx.header.bottom) {
drpcx_uint16 temp = pcx.header.top;
pcx.header.top = pcx.header.bottom;
pcx.header.bottom = temp;
}
pcx.width = pcx.header.right - pcx.header.left + 1;
pcx.height = pcx.header.bottom - pcx.header.top + 1;
pcx.components = (pcx.header.bpp == 8 && pcx.header.bitPlanes == 4) ? 4 : 3;
size_t dataSize = pcx.width * pcx.height * pcx.components;
pcx.pImageData = (drpcx_uint8*)calloc(1, dataSize); // <-- Clearing to zero is important! Required for proper decoding.
if (pcx.pImageData == NULL) {
return NULL; // Failed to allocate memory.
}
drpcx_bool32 result = DRPCX_FALSE;
switch (pcx.header.bpp)
{
case 1:
{
result = drpcx__decode_1bit(&pcx);
} break;
case 2:
{
result = drpcx__decode_2bit(&pcx);
} break;
case 4:
{
result = drpcx__decode_4bit(&pcx);
} break;
case 8:
{
result = drpcx__decode_8bit(&pcx);
} break;
}
if (!result) {
free(pcx.pImageData);
return NULL;
}
// There's an annoying amount of branching when loading PCX files so for simplicity I'm doing the component conversion as
// a second pass.
if (desiredComponents == 0) desiredComponents = pcx.components;
if (desiredComponents != (int)pcx.components) {
drpcx_uint8* pNewImageData = (drpcx_uint8*)malloc(pcx.width * pcx.height * desiredComponents);
if (pNewImageData == NULL) {
free(pcx.pImageData);
return NULL;
}
drpcx_uint8* pSrcData = pcx.pImageData;
drpcx_uint8* pDstData = pNewImageData;
if (desiredComponents < (int)pcx.components) {
// We're reducing the number of components. Just drop the excess.
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
for (int c = 0; c < desiredComponents; ++c) {
pDstData[c] = pSrcData[c];
}
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else {
// We're increasing the number of components. Always ensure the alpha channel is set to 0xFF.
if (pcx.components == 1) {
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
for (int c = 0; c < desiredComponents; ++c) {
pDstData[c] = pSrcData[0];
}
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else if (pcx.components == 2) {
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
pDstData[0] = pSrcData[0];
pDstData[1] = pSrcData[1];
pDstData[2] = 0x00;
if (desiredComponents == 4) pDstData[3] = 0xFF;
pSrcData += pcx.components;
pDstData += desiredComponents;
}
} else {
assert(pcx.components == 3);
assert(desiredComponents == 4);
for (drpcx_uint32 i = 0; i < pcx.width*pcx.height; ++i) {
pDstData[0] = pSrcData[0];
pDstData[1] = pSrcData[1];
pDstData[2] = pSrcData[2];
pDstData[3] = 0xFF;
pSrcData += pcx.components;
pDstData += desiredComponents;
}
}
}
free(pcx.pImageData);
pcx.pImageData = pNewImageData;
}
if (x) *x = pcx.width;
if (y) *y = pcx.height;
if (internalComponents) *internalComponents = pcx.components;
return pcx.pImageData;
}
void drpcx_free(void* pReturnValueFromLoad)
{
free(pReturnValueFromLoad);
}
#endif // DR_PCX_IMPLEMENTATION
// REVISION HISTORY
//
// v0.3.1 - 2018-09-11
// - Styling fixes.
// - Fix a typo.
//
// v0.3 - 2018-02-08
// - API CHANGE: Rename dr_* types to drpcx_*.
//
// v0.2c - 2018-02-07
// - Fix a crash.
//
// v0.2b - 2018-02-02
// - Fix compilation error.
//
// v0.2a - 2017-07-16
// - Change underlying type for booleans to unsigned.
//
// v0.2 - 2016-10-28
// - API CHANGE: Add a parameter to drpcx_load() and family to control the number of output components.
// - Use custom sized types rather than built-in ones to improve support for older MSVC compilers.
//
// v0.1c - 2016-10-23
// - A minor change to drpcx_bool8 and drpcx_bool32 types.
//
// v0.1b - 2016-10-11
// - Use drpcx_bool32 instead of the built-in "bool" type. The reason for this change is that it helps maintain API/ABI consistency
// between C and C++ builds.
//
// v0.1a - 2016-09-18
// - Change date format to ISO 8601 (YYYY-MM-DD)
//
// v0.1 - 2016-05-04
// - Initial versioned release.
// TODO
// - Test 2-bpp/4-plane and 4-bpp/1-plane formats.
/*
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 <http://unlicense.org/>
*/

View file

@ -0,0 +1,49 @@
Building
========
Move into this directory and run the build script for the relevant platform. Run from this directory.
clear && ./build_opus && ./bin/dr_opus_test_0
Alternatively you can compile a specific test manually:
clear && gcc ./opus/dr_opus_test_0 -o ./bin/dr_opus_test_0 && ./bin/dr_opus_test_0
Test vectors will be loaded from the "testvectors" folder, relative to this directory. Therefore, you need to run
each test program from this directory:
./bin/dr_opus_test_0
Building and Running WAV Tests
------------------------------
The WAV tests use libsndfile as a benchmark. The tests programs dynamically link to libsndfile at runtime which
means you don't need to link to it at compile time. However, you will need the headers installed in a standard
location. The batch files for the Windows build will allow you to customize the include path. On the Windows build
you will need to drop two versions of libsndfile-1.dll into the bin directory. For the 32-bit build you need to
name it libsndfile-1-x86.dll and for the 64-bit build you need to name it libsndfile-1-x64.dll.
Test Vectors
============
In order to run certain tests you will need to download test vectors for the relevant project and place them into the
"testvectors" folder.
Opus
----
- Download both the original and new test vectors from https://opus-codec.org/testvectors/ and place them into
the "testvectors/opus" folder.
- Download the Ogg Opus test vectors from https://wiki.xiph.org/OggOpus/testvectors and place them into the
"testvectors/opus" folder.
- The folder structure should like like the following:
- testvectors
- opus
- opus_testvectors
- opus_newvectors
- oggopus
- failure_cases
- opus_multichannel_examples
FLAC
----
- Download the FLAC testbench from https://wiki.hydrogenaud.io/index.php?title=FLAC_decoder_testbench and place
them into the "testvectors/flac/testbench" folder.

View file

@ -0,0 +1,4 @@
#!/bin/sh
gcc ./flac/dr_flac_test_0.c -o ./bin/dr_flac_test_0 -std=c89 -ansi -pedantic -O3 -s -Wall -ldl
gcc ./flac/dr_flac_decoding.c -o ./bin/dr_flac_decoding -std=c89 -ansi -pedantic -Wall -O3 -s -lFLAC -ldl
gcc ./flac/dr_flac_seeking.c -o ./bin/dr_flac_seeking -std=c89 -ansi -pedantic -Wall -O3 -s -lFLAC -ldl

View file

@ -0,0 +1,37 @@
:: NOTES
::
:: These tests use libFLAC as a benchmark. Since Windows doesn't have good standard paths for library files, this
:: script will use "flac/include" as an additional search path for headers and "flac/lib/win32" as an additional
:: search path for libraries. You will need to place FLAC headers in the "flac/include/FLAC" directory and libogg
:: headers int the "flac/include/ogg" directory.
::
:: Examples are linked against "-lFLAC" and "-logg". These need to be placed in a standard directory or "flac/lib/win32".
@echo off
SET c_compiler=gcc
SET cpp_compiler=g++
:: Configure the "arch" option to test different instruction sets.
SET arch=
SET arch=-msse4.1
::SET arch=-mfpu=neon
:: libFLAC and libogg are required for benchmarking.
SET libFLAC=-I./flac/include -L./flac/lib/win32 -lFLAC -logg
:: C options
SET c_options=-std=c89 -ansi
:: C++ options
SET cpp_options=
SET options=-Wall -Wpedantic -pedantic -O3 -s -DNDEBUG %arch% %libFLAC%
SET buildc=%c_compiler% %c_options%
SET buildcpp=%cpp_compiler% %cpp_options%
@echo on
%buildc% ./flac/dr_flac_test_0.c -o ./bin/dr_flac_test_0.exe %options%
%buildcpp% ./flac/dr_flac_test_0.cpp -o ./bin/dr_flac_test_0_cpp.exe %options%
%buildc% ./flac/dr_flac_decoding.c -o ./bin/dr_flac_decoding.exe %options%
%buildcpp% ./flac/dr_flac_decoding.cpp -o ./bin/dr_flac_decoding_cpp.exe %options%

View file

@ -0,0 +1,2 @@
gcc ./mp3/dr_mp3_test_0.c -o ./bin/dr_mp3_test_0.exe -std=c89 -ansi -pedantic -Wall
g++ ./mp3/dr_mp3_test_0.cpp -o ./bin/dr_mp3_test_0.exe -pedantic -Wall

View file

@ -0,0 +1 @@
gcc ./opus/dr_opus_decoding.c -o ./bin/dr_opus_decoding.exe -std=c89 -ansi -pedantic -Wall -Wextra

View file

@ -0,0 +1,38 @@
:: NOTES
::
:: These tests use libsndfile as a benchmark. Since Windows doesn't have good standard paths for library files, this
:: script will use "wav/include" as an additional search path for headers. You will need to place sndfile.h to the
:: "wav/include" directory. Tests will link to libsndfile dynamically at run-time. On the Windows build you'll just
:: need to put a copy of the 32- and 64-bit versions of libsndfile-1.dll into the "bin" directory, with the names
:: libsndfile-1-x86.dll and libsndfile-1-x64.dll respectively. Both versions are required so that both the 32- and
:: 64-bit builds can be tested and benchmarked.
@echo off
SET c_compiler=gcc
SET cpp_compiler=g++
:: Configure the "arch" option to test different instruction sets.
SET arch=
SET arch=-msse4.1
::SET arch=-mfpu=neon
:: libsndfile is required for benchmarking.
SET libsndfile=-I./wav/include
:: C options
SET c_options=-std=c89 -ansi
:: C++ options
SET cpp_options=
SET options=-Wall -Wpedantic -pedantic -O3 -s -DNDEBUG %arch% %libsndfile%
SET buildc=%c_compiler% %c_options% %options%
SET buildcpp=%cpp_compiler% %cpp_options% %options%
@echo on
%buildc% ./wav/dr_wav_test_0.c -o ./bin/dr_wav_test_0.exe
%buildcpp% ./wav/dr_wav_test_0.cpp -o ./bin/dr_wav_test_0_cpp.exe
%buildc% ./wav/dr_wav_decoding.c -o ./bin/dr_wav_decoding.exe
%buildcpp% ./wav/dr_wav_decoding.cpp -o ./bin/dr_wav_decoding_cpp.exe

View file

@ -0,0 +1,957 @@
#ifdef _WIN32
#include <windows.h>
#endif
#if defined(_MSC_VER) || defined(__DMC__)
#else
#include <strings.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <time.h> /* So we can seed the random number generator based on time. */
#include <errno.h>
#if !defined(_WIN32)
#include <dirent.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <dlfcn.h>
#endif
#include <stddef.h> /* For size_t. */
/* Sized types. Prefer built-in types. Fall back to stdint. */
#ifdef _MSC_VER
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlanguage-extension-token"
#pragma GCC diagnostic ignored "-Wlong-long"
#pragma GCC diagnostic ignored "-Wc++11-long-long"
#endif
typedef signed __int8 dr_int8;
typedef unsigned __int8 dr_uint8;
typedef signed __int16 dr_int16;
typedef unsigned __int16 dr_uint16;
typedef signed __int32 dr_int32;
typedef unsigned __int32 dr_uint32;
typedef signed __int64 dr_int64;
typedef unsigned __int64 dr_uint64;
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
#else
#define MA_HAS_STDINT
#include <stdint.h>
typedef int8_t dr_int8;
typedef uint8_t dr_uint8;
typedef int16_t dr_int16;
typedef uint16_t dr_uint16;
typedef int32_t dr_int32;
typedef uint32_t dr_uint32;
typedef int64_t dr_int64;
typedef uint64_t dr_uint64;
#endif
#ifdef MA_HAS_STDINT
typedef uintptr_t dr_uintptr;
#else
#if defined(_WIN32)
#if defined(_WIN64)
typedef dr_uint64 dr_uintptr;
#else
typedef dr_uint32 dr_uintptr;
#endif
#elif defined(__GNUC__)
#if defined(__LP64__)
typedef dr_uint64 dr_uintptr;
#else
typedef dr_uint32 dr_uintptr;
#endif
#else
typedef dr_uint64 dr_uintptr; /* Fallback. */
#endif
#endif
typedef dr_uint8 dr_bool8;
typedef dr_uint32 dr_bool32;
#define DR_TRUE 1
#define DR_FALSE 0
typedef void* dr_handle;
typedef void* dr_ptr;
typedef void (* dr_proc)(void);
#if defined(SIZE_MAX)
#define DR_SIZE_MAX SIZE_MAX
#else
#define DR_SIZE_MAX 0xFFFFFFFF
#endif
/*
Return Values:
0: Success
22: EINVAL
34: ERANGE
Not using symbolic constants for errors because I want to avoid #including errno.h
*/
int dr_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
{
size_t i;
if (dst == 0) {
return 22;
}
if (dstSizeInBytes == 0) {
return 34;
}
if (src == 0) {
dst[0] = '\0';
return 22;
}
for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
dst[i] = src[i];
}
if (i < dstSizeInBytes) {
dst[i] = '\0';
return 0;
}
dst[0] = '\0';
return 34;
}
int dr_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
{
size_t maxcount;
size_t i;
if (dst == 0) {
return 22;
}
if (dstSizeInBytes == 0) {
return 34;
}
if (src == 0) {
dst[0] = '\0';
return 22;
}
maxcount = count;
if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
maxcount = dstSizeInBytes - 1;
}
for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
dst[i] = src[i];
}
if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
dst[i] = '\0';
return 0;
}
dst[0] = '\0';
return 34;
}
int dr_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
{
char* dstorig;
if (dst == 0) {
return 22;
}
if (dstSizeInBytes == 0) {
return 34;
}
if (src == 0) {
dst[0] = '\0';
return 22;
}
dstorig = dst;
while (dstSizeInBytes > 0 && dst[0] != '\0') {
dst += 1;
dstSizeInBytes -= 1;
}
if (dstSizeInBytes == 0) {
return 22; /* Unterminated. */
}
while (dstSizeInBytes > 0 && src[0] != '\0') {
*dst++ = *src++;
dstSizeInBytes -= 1;
}
if (dstSizeInBytes > 0) {
dst[0] = '\0';
} else {
dstorig[0] = '\0';
return 34;
}
return 0;
}
int dr_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
{
char* dstorig;
if (dst == 0) {
return 22;
}
if (dstSizeInBytes == 0) {
return 34;
}
if (src == 0) {
return 22;
}
dstorig = dst;
while (dstSizeInBytes > 0 && dst[0] != '\0') {
dst += 1;
dstSizeInBytes -= 1;
}
if (dstSizeInBytes == 0) {
return 22; /* Unterminated. */
}
if (count == ((size_t)-1)) { /* _TRUNCATE */
count = dstSizeInBytes - 1;
}
while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
*dst++ = *src++;
dstSizeInBytes -= 1;
count -= 1;
}
if (dstSizeInBytes > 0) {
dst[0] = '\0';
} else {
dstorig[0] = '\0';
return 34;
}
return 0;
}
/*
String Helpers
*/
int dr_append_path(char* dst, size_t dstSize, const char* base, const char* other)
{
int err;
size_t len;
/* TODO: Return the correct error codes here. */
if (dst == NULL) {
return -1;
}
if (base == NULL || other == NULL) {
return -1;
}
err = dr_strcpy_s(dst, dstSize, base);
if (err != 0) {
return err;
}
len = strlen(dst);
if (len > 0) {
/* Append the slash if required. */
if (dst[len-1] != '/' && dst[len-1] != '\\') {
err = dr_strcat_s(dst, dstSize, "/");
if (err != 0) {
dst[0] = '\0';
return err;
}
len += 1; /* +1 to the length to account for the slash. */
}
}
err = dr_strcat_s(dst, dstSize, other);
if (err != 0) {
dst[0] = '\0';
return err;
}
/* Success. */
return 0;
}
const char* dr_path_file_name(const char* path)
{
const char* fileName = path;
if (path == NULL) {
return NULL;
}
/* We just loop through the path until we find the last slash. */
while (path[0] != '\0') {
if (path[0] == '/' || path[0] == '\\') {
fileName = path;
}
path += 1;
}
/* At this point the file name is sitting on a slash, so just move forward. */
while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
fileName += 1;
}
return fileName;
}
const char* dr_extension(const char* path)
{
const char* extension = path;
const char* lastoccurance = NULL;
if (path == NULL) {
return NULL;
}
/* Just find the last '.' and return. */
while (extension[0] != '\0') {
if (extension[0] == '.') {
extension += 1;
lastoccurance = extension;
}
extension += 1;
}
return (lastoccurance != 0) ? lastoccurance : extension;
}
dr_bool32 dr_extension_equal(const char* path, const char* extension)
{
const char* ext1;
const char* ext2;
if (path == NULL || extension == NULL) {
return 0;
}
ext1 = extension;
ext2 = dr_extension(path);
#if defined(_MSC_VER) || defined(__DMC__)
return _stricmp(ext1, ext2) == 0;
#else
return strcasecmp(ext1, ext2) == 0;
#endif
}
/*
File Iterator
dr_file_iterator state;
dr_file_iterator* pFile = dr_file_iterator_begin("the/folder/path", &state);
while (pFile != NULL) {
// Do something with pFile.
pFile = dr_file_iterator_next(pFile);
}
Limitations:
- Only supports file paths up to 256 characters.
*/
typedef struct
{
char folderPath[256];
char relativePath[256]; /* Relative to the original folder path. */
char absolutePath[256]; /* Concatenation of folderPath and relativePath. */
dr_bool32 isDirectory;
#ifdef _WIN32
HANDLE hFind;
#else
DIR* dir;
#endif
} dr_file_iterator;
dr_file_iterator* dr_file_iterator_begin(const char* pFolderPath, dr_file_iterator* pState)
{
#ifdef _WIN32
char searchQuery[MAX_PATH];
unsigned int searchQueryLength;
WIN32_FIND_DATAA ffd;
HANDLE hFind;
#else
DIR* dir;
#endif
if (pState == NULL) {
return NULL;
}
memset(pState, 0, sizeof(*pState));
if (pFolderPath == NULL) {
return NULL;
}
#ifdef _WIN32
dr_strcpy_s(searchQuery, sizeof(searchQuery), pFolderPath);
searchQueryLength = (unsigned int)strlen(searchQuery);
if (searchQueryLength >= MAX_PATH - 3) {
return NULL; /* Path is too long. */
}
searchQuery[searchQueryLength + 0] = '\\';
searchQuery[searchQueryLength + 1] = '*';
searchQuery[searchQueryLength + 2] = '\0';
hFind = FindFirstFileA(searchQuery, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return NULL; /* Failed to begin search. */
}
/* Skip past "." and ".." directories. */
while (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) {
if (!FindNextFileA(hFind, &ffd)) {
FindClose(hFind);
return NULL; /* Couldn't find anything. */
}
}
pState->hFind = hFind;
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
pState->isDirectory = 1;
} else {
pState->isDirectory = 0;
}
dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), ffd.cFileName);
#else
dir = opendir(pFolderPath);
if (dir == NULL) {
return NULL; /* Couldn't find anything. */
}
/* Select the first file. */
for (;;) {
struct dirent* info;
struct stat fileinfo;
char filePath[4096];
info = readdir(dir);
if (info == NULL) {
closedir(dir);
return NULL;
}
if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
continue; /* Skip past "." and ".." directories. */
}
dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), info->d_name);
dr_append_path(filePath, sizeof(filePath), pFolderPath, pState->relativePath);
if (stat(filePath, &fileinfo) != 0) {
continue;
}
if (S_ISDIR(fileinfo.st_mode)) {
pState->isDirectory = 1;
} else {
pState->isDirectory = 0;
}
break;
}
pState->dir = dir;
#endif
/* Getting here means everything was successful. We can now set some state before returning. */
dr_strcpy_s(pState->folderPath, sizeof(pState->folderPath), pFolderPath);
dr_append_path(pState->absolutePath, sizeof(pState->absolutePath), pState->folderPath, pState->relativePath);
return pState;
}
dr_file_iterator* dr_file_iterator_next(dr_file_iterator* pState)
{
#ifdef _WIN32
WIN32_FIND_DATAA ffd;
#endif
if (pState == NULL) {
return NULL;
}
#ifdef _WIN32
if (!FindNextFileA(pState->hFind, &ffd)) {
/* Couldn't find anything else. */
FindClose(pState->hFind);
pState->hFind = NULL;
return NULL;
}
/* Found something. */
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
pState->isDirectory = 1;
} else {
pState->isDirectory = 0;
}
dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), ffd.cFileName);
#else
/* Enter a loop here so we can skip past "." and ".." directories. */
for (;;) {
struct dirent* info;
struct stat fileinfo;
char filePath[4096];
info = readdir(pState->dir);
if (info == NULL) {
closedir(pState->dir);
pState->dir = NULL;
return NULL;
}
if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
continue; /* Skip past "." and ".." directories. */
}
dr_strcpy_s(pState->relativePath, sizeof(pState->relativePath), info->d_name);
dr_append_path(filePath, sizeof(filePath), pState->folderPath, pState->relativePath);
/*printf("Done: %s\n", pState->relativePath);*/
if (stat(filePath, &fileinfo) != 0) {
continue;
}
if (S_ISDIR(fileinfo.st_mode)) {
pState->isDirectory = 1;
} else {
pState->isDirectory = 0;
}
break;
}
#endif
/* Success */
dr_append_path(pState->absolutePath, sizeof(pState->absolutePath), pState->folderPath, pState->relativePath);
return pState;
}
void dr_file_iterator_end(dr_file_iterator* pState)
{
if (pState == NULL) {
return;
}
#ifdef _WIN32
FindClose(pState->hFind);
pState->hFind = NULL;
#else
closedir(pState->dir);
pState->dir = NULL;
#endif
}
/*
File Management
Free file data with free().
*/
static int dr_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
{
#if defined(_MSC_VER) && _MSC_VER >= 1400
errno_t err;
#endif
if (ppFile != NULL) {
*ppFile = NULL; /* Safety. */
}
if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
return -1; /* Invalid args. */
}
#if defined(_MSC_VER) && _MSC_VER >= 1400
err = fopen_s(ppFile, pFilePath, pOpenMode);
if (err != 0) {
return err;
}
#else
#if defined(_WIN32) || defined(__APPLE__)
*ppFile = fopen(pFilePath, pOpenMode);
#else
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
*ppFile = fopen64(pFilePath, pOpenMode);
#else
*ppFile = fopen(pFilePath, pOpenMode);
#endif
#endif
if (*ppFile == NULL) {
return errno;
}
#endif
return 0;
}
void* dr_open_and_read_file_with_extra_data(const char* pFilePath, size_t* pFileSizeOut, size_t extraBytes)
{
FILE* pFile;
size_t fileSize;
size_t bytesRead;
void* pFileData;
/* Safety. */
if (pFileSizeOut) {
*pFileSizeOut = 0;
}
if (pFilePath == NULL) {
return NULL;
}
/* TODO: Use 64-bit versions of the FILE APIs. */
if (dr_fopen(&pFile, pFilePath, "rb") != 0) {
return NULL;
}
fseek(pFile, 0, SEEK_END);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
/* Need to make sure we have enough room for the extra bytes, if any. */
if (fileSize == DR_SIZE_MAX && extraBytes > 0) {
fclose(pFile);
return NULL; /* File is too big. */
}
pFileData = malloc((size_t)fileSize + extraBytes); /* <-- Safe cast due to the check above. */
if (pFileData == NULL) {
fclose(pFile);
return NULL; /* Failed to allocate memory for the file. Good chance the file is too big. */
}
bytesRead = fread(pFileData, 1, (size_t)fileSize, pFile);
if (bytesRead != fileSize) {
free(pFileData);
fclose(pFile);
return NULL; /* Failed to read every byte from the file. */
}
fclose(pFile);
if (pFileSizeOut) {
*pFileSizeOut = (size_t)fileSize;
}
return pFileData;
}
void* dr_open_and_read_file(const char* pFilePath, size_t* pFileSizeOut)
{
return dr_open_and_read_file_with_extra_data(pFilePath, pFileSizeOut, 0);
}
dr_bool32 dr_argv_is_set(int argc, char** argv, const char* value)
{
int iarg;
for (iarg = 0; iarg < argc; ++iarg) {
if (strcmp(argv[iarg], value) == 0) {
return DR_TRUE;
}
}
return DR_FALSE;
}
int dr_vprintf_fixed(int width, const char* const format, va_list args)
{
int i;
int len;
char buffer[4096];
if (width <= 0) {
return -1; /* Width cannot be negative or 0. */
}
if ((unsigned int)width > sizeof(buffer)) {
return -1; /* Width is too big. */
}
/* We need to print this into a string (truncated). */
#if (defined(_MSC_VER) && _MSC_VER > 1200) || ((defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) || defined(_ISOC99_SOURCE) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L))
len = vsnprintf(buffer, width+1, format, args);
#else
len = vsprintf(buffer, format, args);
if (len > width) {
len = width;
}
buffer[len] = '\0';
#endif
printf("%s", buffer);
for (i = len; i < width; ++i) {
printf(" ");
}
return len;
}
int dr_printf_fixed(int width, const char* const format, ...)
{
int result;
va_list args;
va_start(args, format);
{
result = dr_vprintf_fixed(width, format, args);
}
va_end(args);
return result;
}
int dr_vprintf_fixed_with_margin(int width, int margin, const char* const format, va_list args)
{
int i;
/* Margin. */
for (i = 0; i < margin; ++i) {
printf(" ");
}
return dr_vprintf_fixed(width - margin, format, args);
}
int dr_printf_fixed_with_margin(int width, int margin, const char* const format, ...)
{
int result;
va_list args;
va_start(args, format);
{
result = dr_vprintf_fixed_with_margin(width, margin, format, args);
}
va_end(args);
return result;
}
#ifdef _WIN32
static LARGE_INTEGER g_DRTimerFrequency = {{0}};
double dr_timer_now()
{
LARGE_INTEGER counter;
if (g_DRTimerFrequency.QuadPart == 0) {
QueryPerformanceFrequency(&g_DRTimerFrequency);
}
QueryPerformanceCounter(&counter);
return counter.QuadPart / (double)g_DRTimerFrequency.QuadPart;
}
#else
#if _POSIX_C_SOURCE >= 199309L
#if defined(CLOCK_MONOTONIC)
#define MA_CLOCK_ID CLOCK_MONOTONIC
#else
#define MA_CLOCK_ID CLOCK_REALTIME
#endif
double dr_timer_now()
{
struct timespec newTime;
clock_gettime(CLOCK_MONOTONIC, &newTime);
return ((newTime.tv_sec * 1000000000LL) + newTime.tv_nsec) / 1000000000.0;
}
#else
double dr_timer_now()
{
struct timeval newTime;
gettimeofday(&newTime, NULL);
return ((newTime.tv_sec * 1000000) + newTime.tv_usec) / 1000000.0;
}
#endif
#endif
float dr_scale_to_range_f32(float x, float lo, float hi)
{
return lo + x*(hi-lo);
}
#define DR_LCG_M 2147483647
#define DR_LCG_A 48271
#define DR_LCG_C 0
static int g_drLCG;
void dr_seed(int seed)
{
g_drLCG = seed;
}
int dr_rand_s32()
{
int lcg = g_drLCG;
int r = (DR_LCG_A * lcg + DR_LCG_C) % DR_LCG_M;
g_drLCG = r;
return r;
}
unsigned int dr_rand_u32()
{
return (unsigned int)dr_rand_s32();
}
dr_uint64 dr_rand_u64()
{
return ((dr_uint64)dr_rand_u32() << 32) | dr_rand_u32();
}
double dr_rand_f64()
{
return dr_rand_s32() / (double)0x7FFFFFFF;
}
float dr_rand_f32()
{
return (float)dr_rand_f64();
}
float dr_rand_range_f32(float lo, float hi)
{
return dr_scale_to_range_f32(dr_rand_f32(), lo, hi);
}
int dr_rand_range_s32(int lo, int hi)
{
if (lo == hi) {
return lo;
}
return lo + dr_rand_u32() / (0xFFFFFFFF / (hi - lo + 1) + 1);
}
dr_uint64 dr_rand_range_u64(dr_uint64 lo, dr_uint64 hi)
{
if (lo == hi) {
return lo;
}
return lo + dr_rand_u64() / ((~(dr_uint64)0) / (hi - lo + 1) + 1);
}
void dr_pcm_s32_to_f32(void* dst, const void* src, dr_uint64 count)
{
float* dst_f32 = (float*)dst;
const dr_int32* src_s32 = (const dr_int32*)src;
dr_uint64 i;
for (i = 0; i < count; i += 1) {
double x = src_s32[i];
#if 0
x = x + 2147483648.0;
x = x * 0.0000000004656612873077392578125;
x = x - 1;
#else
x = x / 2147483648.0;
#endif
dst_f32[i] = (float)x;
}
}
void dr_pcm_s32_to_s16(void* dst, const void* src, dr_uint64 count)
{
dr_int16* dst_s16 = (dr_int16*)dst;
const dr_int32* src_s32 = (const dr_int32*)src;
dr_uint64 i;
for (i = 0; i < count; i += 1) {
dr_int32 x = src_s32[i];
x = x >> 16;
dst_s16[i] = (dr_int16)x;
}
}
dr_handle dr_dlopen(const char* filename)
{
dr_handle handle;
#ifdef _WIN32
handle = (dr_handle)LoadLibraryA(filename);
#else
handle = (dr_handle)dlopen(filename, RTLD_NOW);
#endif
return handle;
}
void dr_dlclose(dr_handle handle)
{
#ifdef _WIN32
FreeLibrary((HMODULE)handle);
#else
dlclose((void*)handle);
#endif
}
dr_proc dr_dlsym(dr_handle handle, const char* symbol)
{
dr_proc proc;
#ifdef _WIN32
proc = (dr_proc)GetProcAddress((HMODULE)handle, symbol);
#else
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
proc = (dr_proc)dlsym((void*)handle, symbol);
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif
#endif
return proc;
}

@ -0,0 +1 @@
Subproject commit d1a166c83ab445b1c14bc83d37c84e18d172e5f5

View file

@ -0,0 +1,332 @@
#define DR_FLAC_IMPLEMENTATION
#include "../../dr_flac.h"
/* libFLAC has a warning in their header. *sigh*. */
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wc99-extensions"
#endif
#endif
#include <FLAC/stream_decoder.h>
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#include "../common/dr_common.c"
/*
The libflac object is used to make it easier to compare things against the reference implementation. I don't think
libFLAC's API is all that easy to use or intuitive so I'm wrapping it. This is deliberately simple. It decodes the
entire file into memory upon initialization.
*/
typedef struct
{
drflac_int32* pPCMFrames; /* Interleaved. */
drflac_uint64 pcmFrameCount;
drflac_uint64 pcmFrameCap; /* The capacity of the pPCMFrames buffer in PCM frames. */
drflac_uint32 channels;
drflac_uint32 sampleRate;
drflac_uint64 currentPCMFrame; /* The index of the PCM frame the decoder is currently sitting on. */
double decodeTimeInSeconds; /* The total amount of time it took to decode the file. This is used for profiling. */
drflac_uint8* pFileData;
size_t fileSizeInBytes;
size_t fileReadPos;
} libflac;
static FLAC__StreamDecoderReadStatus libflac__read_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__byte buffer[], size_t *bytes, void *client_data)
{
libflac* pDecoder = (libflac*)client_data;
size_t bytesToRead = *bytes;
size_t bytesRemaining = pDecoder->fileSizeInBytes - pDecoder->fileReadPos;
(void)pStreamDecoder;
if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining;
}
if (bytesToRead > 0) {
memcpy(buffer, pDecoder->pFileData + pDecoder->fileReadPos, bytesToRead);
pDecoder->fileReadPos += bytesToRead;
}
*bytes = bytesToRead;
return (bytesToRead == 0 && bytesRemaining == 0) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
static FLAC__StreamDecoderWriteStatus libflac__write_callback(const FLAC__StreamDecoder *pStreamDecoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
{
libflac* pDecoder = (libflac*)client_data;
drflac_uint32 pcmFramesInFLACFrame;
drflac_int32* pNextSampleInNewFrame;
drflac_uint32 i, j;
(void)pStreamDecoder;
pcmFramesInFLACFrame = frame->header.blocksize;
/* Make sure there's room in the buffer. */
if ((pDecoder->pcmFrameCount + pcmFramesInFLACFrame) > pDecoder->pcmFrameCap) {
drflac_int32* pNewPCMFrames;
pDecoder->pcmFrameCap *= 2;
if (pDecoder->pcmFrameCap < (pDecoder->pcmFrameCount + pcmFramesInFLACFrame)) {
pDecoder->pcmFrameCap = (pDecoder->pcmFrameCount + pcmFramesInFLACFrame);
}
pNewPCMFrames = (drflac_int32*)realloc(pDecoder->pPCMFrames, (size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
if (pNewPCMFrames == NULL) {
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
pDecoder->pPCMFrames = pNewPCMFrames;
}
/* Make sure the decoded samples are interleaved. */
pNextSampleInNewFrame = pDecoder->pPCMFrames + (pDecoder->pcmFrameCount * pDecoder->channels);
for (i = 0; i < pcmFramesInFLACFrame; i += 1) {
for (j = 0; j < pDecoder->channels; ++j) {
*pNextSampleInNewFrame++ = buffer[j][i] << (32 - frame->header.bits_per_sample);
}
}
pDecoder->pcmFrameCount += pcmFramesInFLACFrame;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
static FLAC__StreamDecoderLengthStatus libflac__length_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__uint64 *stream_length, void *client_data)
{
libflac* pDecoder = (libflac*)client_data;
(void)pStreamDecoder;
*stream_length = pDecoder->fileSizeInBytes;
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
static void libflac__metadata_callback(const FLAC__StreamDecoder *pStreamDecoder, const FLAC__StreamMetadata *metadata, void *client_data)
{
libflac* pDecoder = (libflac*)client_data;
(void)pStreamDecoder;
/* Here is where we initialize the buffer for the decoded data. */
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
pDecoder->pcmFrameCount = 0; /* Always set this to 0. Don't be tempted to set it to metadata->data.stream_info.total_samples. It's increment in libflac__write_callback. */
pDecoder->channels = metadata->data.stream_info.channels;
pDecoder->sampleRate = metadata->data.stream_info.sample_rate;
/* Allocate an initial block of memory if we know the total size. */
if (metadata->data.stream_info.total_samples > 0) {
pDecoder->pcmFrameCap = metadata->data.stream_info.total_samples;
pDecoder->pPCMFrames = (drflac_int32*)malloc((size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
if (pDecoder->pPCMFrames != NULL) {
DRFLAC_ZERO_MEMORY(pDecoder->pPCMFrames, (size_t)(pDecoder->pcmFrameCap * pDecoder->channels * sizeof(drflac_int32)));
}
}
}
}
static void libflac__error_callback(const FLAC__StreamDecoder *pStreamDecoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
(void)pStreamDecoder;
(void)status;
(void)client_data;
}
/*
Initializes a libflac object.
This will perform a full decode of the file using libFLAC. The time it takes to decode the file will be stored in decodeTimeInSeconds
for the purpose of profiling. The decoding process will load the entire file into memory before calling into libFLAC. This way profiling
will exclude any IO time.
*/
drflac_result libflac_init_file(const char* pFilePath, libflac* pDecoder)
{
FLAC__StreamDecoder* pStreamDecoder;
FLAC__bool libflacResult;
FLAC__StreamDecoderInitStatus libflacStatus;
double decodeTimeBeg;
double decodeTimeEnd;
DRFLAC_ZERO_MEMORY(pDecoder, sizeof(*pDecoder));
pDecoder->pFileData = (drflac_uint8*)dr_open_and_read_file(pFilePath, &pDecoder->fileSizeInBytes);
if (pDecoder->pFileData == NULL) {
return DRFLAC_ERROR; /* Failed to open the file. */
}
/* Initialize the libFLAC decoder. */
pStreamDecoder = FLAC__stream_decoder_new();
if (pDecoder == NULL) {
return DRFLAC_ERROR; /* Failed to create a new stream decoder. Out of memory. */
}
if (dr_extension_equal(pFilePath, "ogg") || dr_extension_equal(pFilePath, "oga") || dr_extension_equal(pFilePath, "ogv")) {
libflacStatus = FLAC__stream_decoder_init_ogg_stream(pStreamDecoder, libflac__read_callback, NULL, NULL, libflac__length_callback, NULL, libflac__write_callback, libflac__metadata_callback, libflac__error_callback, pDecoder);
} else {
libflacStatus = FLAC__stream_decoder_init_stream(pStreamDecoder, libflac__read_callback, NULL, NULL, libflac__length_callback, NULL, libflac__write_callback, libflac__metadata_callback, libflac__error_callback, pDecoder);
}
if (libflacStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
FLAC__stream_decoder_delete(pStreamDecoder);
free(pDecoder->pFileData);
return DRFLAC_ERROR; /* Failed to initialize the stream. */
}
/* Read past the metadata first. This will fire the libflac__metadata_callback which will do a bit of initialization for us. */
libflacResult = FLAC__stream_decoder_process_until_end_of_metadata(pStreamDecoder);
if (libflacResult == DRFLAC_FALSE) {
FLAC__stream_decoder_delete(pStreamDecoder);
free(pDecoder->pFileData);
return DRFLAC_ERROR; /* Failed to read metadata from the FLAC stream. */
}
/* Now we can just decode the entire file. */
decodeTimeBeg = dr_timer_now();
libflacResult = FLAC__stream_decoder_process_until_end_of_stream(pStreamDecoder);
decodeTimeEnd = dr_timer_now();
pDecoder->decodeTimeInSeconds = decodeTimeEnd - decodeTimeBeg;
/* We're done with the libFLAC decoder. */
FLAC__stream_decoder_delete(pStreamDecoder);
pStreamDecoder = NULL;
/* Free the file data. */
free(pDecoder->pFileData);
pDecoder->pFileData = NULL;
pDecoder->fileSizeInBytes = 0;
pDecoder->fileReadPos = 0;
if (libflacResult == DRFLAC_FALSE) {
return DRFLAC_ERROR; /* Some error ocurred while decoding. */
}
return DRFLAC_SUCCESS;
}
void libflac_uninit(libflac* pDecoder)
{
if (pDecoder == NULL) {
return;
}
free(pDecoder->pPCMFrames);
}
drflac_uint64 libflac_read_pcm_frames_s32(libflac* pDecoder, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
{
drflac_uint64 pcmFramesRemaining;
if (pDecoder == NULL) {
return 0;
}
pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
if (framesToRead > pcmFramesRemaining) {
framesToRead = pcmFramesRemaining;
}
if (framesToRead == 0) {
return 0;
}
DRFLAC_COPY_MEMORY(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), (size_t)(framesToRead * pDecoder->channels * sizeof(drflac_int32)));
pDecoder->currentPCMFrame += framesToRead;
return framesToRead;
}
drflac_uint64 libflac_read_pcm_frames_f32(libflac* pDecoder, drflac_uint64 framesToRead, float* pBufferOut)
{
drflac_uint64 pcmFramesRemaining;
if (pDecoder == NULL) {
return 0;
}
pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
if (framesToRead > pcmFramesRemaining) {
framesToRead = pcmFramesRemaining;
}
if (framesToRead == 0) {
return 0;
}
/* s32 to f32 */
dr_pcm_s32_to_f32(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), framesToRead * pDecoder->channels);
pDecoder->currentPCMFrame += framesToRead;
return framesToRead;
}
drflac_uint64 libflac_read_pcm_frames_s16(libflac* pDecoder, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
{
drflac_uint64 pcmFramesRemaining;
if (pDecoder == NULL) {
return 0;
}
pcmFramesRemaining = pDecoder->pcmFrameCount - pDecoder->currentPCMFrame;
if (framesToRead > pcmFramesRemaining) {
framesToRead = pcmFramesRemaining;
}
if (framesToRead == 0) {
return 0;
}
/* s32 to f32 */
dr_pcm_s32_to_s16(pBufferOut, pDecoder->pPCMFrames + (pDecoder->currentPCMFrame * pDecoder->channels), framesToRead * pDecoder->channels);
pDecoder->currentPCMFrame += framesToRead;
return framesToRead;
}
drflac_bool32 libflac_seek_to_pcm_frame(libflac* pDecoder, drflac_uint64 targetPCMFrameIndex)
{
if (pDecoder == NULL) {
return DRFLAC_FALSE;
}
if (targetPCMFrameIndex > pDecoder->pcmFrameCount) {
return DRFLAC_FALSE; /* Trying to seek too far forward. */
}
pDecoder->currentPCMFrame = targetPCMFrameIndex;
return DRFLAC_TRUE;
}
/* Helper for printing CPU caps from dr_flac. */
void print_cpu_caps()
{
#if defined(DRFLAC_64BIT)
printf("64 Bit\n");
#else
printf("32 Bit\n");
#endif
drflac__init_cpu_caps();
#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
#if defined(DRFLAC_X64)
printf("Architecture: x64\n");
#else
printf("Architecture: x86\n");
#endif
printf("Has SSE2: %s\n", drflac_has_sse2() ? "YES" : "NO");
printf("Has SSE4.1: %s\n", drflac_has_sse41() ? "YES" : "NO");
printf("Has LZCNT: %s\n", drflac__is_lzcnt_supported() ? "YES" : "NO");
#endif
}

View file

@ -0,0 +1,825 @@
/*#define DR_FLAC_NO_CRC*/
/*#define DR_FLAC_NO_SIMD*/
/*#define DR_FLAC_BUFFER_SIZE 4096*/
#include "dr_flac_common.c"
#define FILE_NAME_WIDTH 40
#define NUMBER_WIDTH 10
#define TABLE_MARGIN 2
#define DEFAULT_SOURCE_DIR "testvectors/flac/testbench"
drflac_result decode_test__read_and_compare_pcm_frames_s32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, drflac_int32* pPCMFrames_libflac, drflac_int32* pPCMFrames_drflac)
{
drflac_uint64 pcmFrameCount_libflac;
drflac_uint64 pcmFrameCount_drflac;
drflac_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libflac = libflac_read_pcm_frames_s32(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
pcmFrameCount_drflac = drflac_read_pcm_frames_s32(pFlac, pcmFrameCount, pPCMFrames_drflac);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pcmFrameCount_libflac, (int)pcmFrameCount_drflac);
return DRFLAC_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
drflac_int32* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d\n", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRFLAC_SUCCESS;
}
drflac_result decode_test__read_and_compare_pcm_frame_chunks_s32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_uint64 iPCMFrame;
drflac_int32* pPCMFrames_libflac;
drflac_int32* pPCMFrames_drflac;
/* Make sure the decoder's are seeked back to the start first. */
drflac_seek_to_pcm_frame(pFlac, 0);
libflac_seek_to_pcm_frame(pLibFlac, 0);
pPCMFrames_libflac = (drflac_int32*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int32)));
if (pPCMFrames_libflac == NULL) {
printf(" [libFLAC] Out of memory");
return DRFLAC_ERROR;
}
pPCMFrames_drflac = (drflac_int32*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int32)));
if (pPCMFrames_drflac == NULL) {
free(pPCMFrames_libflac);
printf(" [dr_flac] Out of memory");
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_s32(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
if (result != DRFLAC_SUCCESS) {
break;
}
}
free(pPCMFrames_libflac);
free(pPCMFrames_drflac);
return result;
}
drflac_result decode_test__read_and_compare_pcm_frames_f32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, float* pPCMFrames_libflac, float* pPCMFrames_drflac)
{
drflac_uint64 pcmFrameCount_libflac;
drflac_uint64 pcmFrameCount_drflac;
drflac_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libflac = libflac_read_pcm_frames_f32(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
pcmFrameCount_drflac = drflac_read_pcm_frames_f32(pFlac, pcmFrameCount, pPCMFrames_drflac);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
return DRFLAC_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
float* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
float* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRFLAC_SUCCESS;
}
drflac_result decode_test__read_and_compare_pcm_frame_chunks_f32(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_uint64 iPCMFrame;
float* pPCMFrames_libflac;
float* pPCMFrames_drflac;
/* Make sure the decoder's are seeked back to the start first. */
drflac_seek_to_pcm_frame(pFlac, 0);
libflac_seek_to_pcm_frame(pLibFlac, 0);
pPCMFrames_libflac = (float*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(float)));
if (pPCMFrames_libflac == NULL) {
printf(" [libFLAC] Out of memory");
return DRFLAC_ERROR;
}
pPCMFrames_drflac = (float*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(float)));
if (pPCMFrames_drflac == NULL) {
free(pPCMFrames_libflac);
printf(" [dr_flac] Out of memory");
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_f32(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
if (result != DRFLAC_SUCCESS) {
break;
}
}
free(pPCMFrames_libflac);
free(pPCMFrames_drflac);
return result;
}
drflac_result decode_test__read_and_compare_pcm_frames_s16(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameCount, drflac_int16* pPCMFrames_libflac, drflac_int16* pPCMFrames_drflac)
{
drflac_uint64 pcmFrameCount_libflac;
drflac_uint64 pcmFrameCount_drflac;
drflac_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libflac = libflac_read_pcm_frames_s16(pLibFlac, pcmFrameCount, pPCMFrames_libflac);
pcmFrameCount_drflac = drflac_read_pcm_frames_s16(pFlac, pcmFrameCount, pPCMFrames_drflac);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
return DRFLAC_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
drflac_int16* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
drflac_int16* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRFLAC_SUCCESS;
}
drflac_result decode_test__read_and_compare_pcm_frame_chunks_s16(libflac* pLibFlac, drflac* pFlac, drflac_uint64 pcmFrameChunkSize)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_uint64 iPCMFrame;
drflac_int16* pPCMFrames_libflac;
drflac_int16* pPCMFrames_drflac;
/* Make sure the decoder's are seeked back to the start first. */
drflac_seek_to_pcm_frame(pFlac, 0);
libflac_seek_to_pcm_frame(pLibFlac, 0);
pPCMFrames_libflac = (drflac_int16*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int16)));
if (pPCMFrames_libflac == NULL) {
printf(" [libFLAC] Out of memory");
return DRFLAC_ERROR;
}
pPCMFrames_drflac = (drflac_int16*)malloc((size_t)(pcmFrameChunkSize * pLibFlac->channels * sizeof(drflac_int16)));
if (pPCMFrames_drflac == NULL) {
free(pPCMFrames_libflac);
printf(" [dr_flac] Out of memory");
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_s16(pLibFlac, pFlac, pcmFrameChunkSize, pPCMFrames_libflac, pPCMFrames_drflac);
if (result != DRFLAC_SUCCESS) {
break;
}
}
free(pPCMFrames_libflac);
free(pPCMFrames_drflac);
return result;
}
drflac_result decode_test_file_s32(libflac* pLibFlac, drflac* pFlac)
{
drflac_result result = DRFLAC_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, 1);
}
/* Now test FLAC frame boundaries. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
}
return result;
}
drflac_result decode_test_file_f32(libflac* pLibFlac, drflac* pFlac)
{
drflac_result result = DRFLAC_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, 1);
}
/* Now test FLAC frame boundaries. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
}
return result;
}
drflac_result decode_test_file_s16(libflac* pLibFlac, drflac* pFlac)
{
drflac_result result = DRFLAC_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, pLibFlac->pcmFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, 1);
}
/* Now test FLAC frame boundaries. */
if (result == DRFLAC_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pLibFlac, pFlac, (pFlac->maxBlockSizeInPCMFrames > 0) ? pFlac->maxBlockSizeInPCMFrames : 4096);
}
return result;
}
static void on_meta(void* pUserData, drflac_metadata* pMetadata)
{
(void)pUserData;
switch (pMetadata->type)
{
case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
{
/*
This section is here just to make it easy to test cuesheets. It's not designed to integrate cleanly with the output of this program
so I'm just selectively enabling and disabling this section as required.
*/
#if 0
drflac_cuesheet_track_iterator i;
drflac_cuesheet_track track;
printf("Cuesheet Found. Track Count = %d\n", (int)pMetadata->data.cuesheet.trackCount);
drflac_init_cuesheet_track_iterator(&i, pMetadata->data.cuesheet.trackCount, pMetadata->data.cuesheet.pTrackData);
while (drflac_next_cuesheet_track(&i, &track)) {
drflac_uint32 iTrackIndex;
printf("Cuesheet Track %d. Index Count = %d:\n", track.trackNumber, track.indexCount);
for (iTrackIndex = 0; iTrackIndex < track.indexCount; iTrackIndex += 1) {
printf(" Index %d - Offset %llu\n", iTrackIndex, track.pIndexPoints[iTrackIndex].offset);
}
}
#endif
} break;
}
}
drflac_result decode_test_file(const char* pFilePath)
{
/* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libFLAC. */
drflac_result result;
libflac libflac;
drflac* pFlac;
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
/* First load the decoder from libFLAC. */
result = libflac_init_file(pFilePath, &libflac);
if (result != DRFLAC_SUCCESS) {
printf(" Failed to open via libFLAC.");
return result;
}
/* Now load from dr_flac. */
pFlac = drflac_open_file_with_metadata(pFilePath, on_meta, NULL, NULL);
if (pFlac == NULL) {
printf(" Failed to open via dr_flac.");
libflac_uninit(&libflac);
return DRFLAC_ERROR; /* Failed to load dr_flac decoder. */
}
/* At this point we should have both libFLAC and dr_flac decoders open. We can now perform identical operations on each of them and compare. */
result = decode_test_file_s32(&libflac, pFlac);
if (result != DRFLAC_SUCCESS) {
drflac_close(pFlac);
libflac_uninit(&libflac);
return result;
}
result = decode_test_file_f32(&libflac, pFlac);
if (result != DRFLAC_SUCCESS) {
drflac_close(pFlac);
libflac_uninit(&libflac);
return result;
}
result = decode_test_file_s16(&libflac, pFlac);
if (result != DRFLAC_SUCCESS) {
drflac_close(pFlac);
libflac_uninit(&libflac);
return result;
}
/* We're done with our decoders. */
drflac_close(pFlac);
libflac_uninit(&libflac);
if (result == DRFLAC_SUCCESS) {
printf(" Passed");
}
return result;
}
drflac_result decode_test_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = decode_test_file(pFile->absolutePath);
(void)result;
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRFLAC_SUCCESS;
}
drflac_result decode_test()
{
drflac_result result = DRFLAC_SUCCESS;
/* Directories. */
{
result = decode_test_directory(DEFAULT_SOURCE_DIR);
(void)result;
}
return result;
}
drflac_result open_and_read_test_file_s32(libflac* pLibFlac, const char* pFilePath)
{
drflac_int32* pPCMFrames;
drflac_uint64 pcmFrameCount;
drflac_uint32 channels;
drflac_uint32 sampleRate;
drflac_uint64 iPCMFrame;
pPCMFrames = drflac_open_file_and_read_pcm_frames_s32(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
if (pPCMFrames == NULL) {
printf(" drflac_open_and_read failed.");
return DRFLAC_ERROR; /* Error decoding */
}
if (pcmFrameCount != pLibFlac->pcmFrameCount) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_int32* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
drflac_free(pPCMFrames, NULL);
return DRFLAC_SUCCESS;
}
drflac_result open_and_read_test_file_f32(libflac* pLibFlac, const char* pFilePath)
{
float* pPCMFrames;
drflac_uint64 pcmFrameCount;
drflac_uint32 channels;
drflac_uint32 sampleRate;
drflac_uint64 iPCMFrame;
pPCMFrames = drflac_open_file_and_read_pcm_frames_f32(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
if (pPCMFrames == NULL) {
printf(" drflac_open_and_read failed.");
return DRFLAC_ERROR; /* Error decoding */
}
if (pcmFrameCount != pLibFlac->pcmFrameCount) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
float* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if ((pPCMFrame_libflac[iChannel] / 2147483648.0) != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
drflac_free(pPCMFrames, NULL);
return DRFLAC_SUCCESS;
}
drflac_result open_and_read_test_file_s16(libflac* pLibFlac, const char* pFilePath)
{
drflac_int16* pPCMFrames;
drflac_uint64 pcmFrameCount;
drflac_uint32 channels;
drflac_uint32 sampleRate;
drflac_uint64 iPCMFrame;
pPCMFrames = drflac_open_file_and_read_pcm_frames_s16(pFilePath, &channels, &sampleRate, &pcmFrameCount, NULL);
if (pPCMFrames == NULL) {
printf(" drflac_open_and_read failed.");
return DRFLAC_ERROR; /* Error decoding */
}
if (pcmFrameCount != pLibFlac->pcmFrameCount) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libFLAC=%d, dr_flac=%d", (int)pcmFrameCount, (int)pLibFlac->currentPCMFrame, (int)pcmFrameCount);
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pLibFlac->pcmFrameCount; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pLibFlac->pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_int16* pPCMFrame_drflac = pPCMFrames + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if ((pPCMFrame_libflac[iChannel] >> 16) != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
drflac_free(pPCMFrames, NULL);
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
drflac_free(pPCMFrames, NULL);
return DRFLAC_SUCCESS;
}
drflac_result open_and_read_test_file(const char* pFilePath)
{
drflac_result result;
libflac libflac;
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
/* First load the decoder from libFLAC. */
result = libflac_init_file(pFilePath, &libflac);
if (result != DRFLAC_SUCCESS) {
printf(" Failed to open via libFLAC.");
return result;
}
result = open_and_read_test_file_s32(&libflac, pFilePath);
if (result != DRFLAC_SUCCESS) {
return result;
}
open_and_read_test_file_f32(&libflac, pFilePath);
if (result != DRFLAC_SUCCESS) {
return result;
}
open_and_read_test_file_s16(&libflac, pFilePath);
if (result != DRFLAC_SUCCESS) {
return result;
}
libflac_uninit(&libflac);
if (result == DRFLAC_SUCCESS) {
printf(" Passed");
}
return result;
}
drflac_result open_and_read_test_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = open_and_read_test_file(pFile->absolutePath);
(void)result;
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRFLAC_SUCCESS;
}
drflac_result open_and_read_test()
{
drflac_result result = DRFLAC_SUCCESS;
/* Directories. */
{
result = open_and_read_test_directory(DEFAULT_SOURCE_DIR);
(void)result;
}
return result;
}
drflac_result decode_profiling_file(const char* pFilePath)
{
drflac_result result;
libflac libflac;
drflac* pFlac;
drflac_int32* pTempBuffer;
double decodeTimeBeg;
double decodeTimeEnd;
double drflacDecodeTimeInSeconds;
void* pFileData;
size_t fileSizeInBytes;
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, 2, "%s", dr_path_file_name(pFilePath));
/* libFLAC */
result = libflac_init_file(pFilePath, &libflac);
if (result != DRFLAC_SUCCESS) {
printf(" [libFLAC] Failed to load file");
return result;
}
/* dr_flac */
pFileData = dr_open_and_read_file(pFilePath, &fileSizeInBytes);
if (pFileData == NULL) {
printf(" Failed to load file");
return DRFLAC_ERROR; /* Failed to open the file. */
}
pFlac = drflac_open_memory(pFileData, fileSizeInBytes, NULL);
if (pFlac == NULL) {
free(pFileData);
printf(" [dr_flac] Failed to load file.");
return DRFLAC_ERROR;
}
/* libFLAC decode time. */
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%.2fms", libflac.decodeTimeInSeconds*1000);
/* dr_flac decode time. */
pTempBuffer = (drflac_int32*)malloc((size_t)(libflac.pcmFrameCount * libflac.channels * sizeof(drflac_int32)));
if (pTempBuffer == NULL) {
libflac_uninit(&libflac);
drflac_close(pFlac);
free(pFileData);
printf(" Out of memory.");
return DRFLAC_ERROR; /* Out of memory. */
}
DRFLAC_ZERO_MEMORY(pTempBuffer, (size_t)(libflac.pcmFrameCount * libflac.channels * sizeof(drflac_int32)));
decodeTimeBeg = dr_timer_now();
drflac_read_pcm_frames_s32(pFlac, libflac.pcmFrameCount, pTempBuffer);
decodeTimeEnd = dr_timer_now();
free(pTempBuffer);
free(pFileData);
drflacDecodeTimeInSeconds = decodeTimeEnd - decodeTimeBeg;
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%.2fms", drflacDecodeTimeInSeconds*1000);
/* Difference. */
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "%d%%", (int)(drflacDecodeTimeInSeconds/libflac.decodeTimeInSeconds * 100));
libflac_uninit(&libflac);
drflac_close(pFlac);
return DRFLAC_SUCCESS;
}
drflac_result decode_profiling_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
drflac_bool32 foundError = DRFLAC_FALSE;
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "libFLAC");
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "dr_flac");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = decode_profiling_file(pFile->absolutePath);
if (result != DRFLAC_SUCCESS) {
foundError = DRFLAC_TRUE;
}
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return (foundError) ? DRFLAC_ERROR : DRFLAC_SUCCESS;
}
drflac_result decode_profiling()
{
drflac_result result = DRFLAC_SUCCESS;
/* Directories. */
{
result = decode_profiling_directory(DEFAULT_SOURCE_DIR);
}
return result;
}
int main(int argc, char** argv)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_bool32 doTesting = DRFLAC_TRUE;
drflac_bool32 doProfiling = DRFLAC_TRUE;
/* This program has two main parts. The first is just a normal functionality test. The second is a profiling of the different seeking methods. */
if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
doTesting = DRFLAC_FALSE;
}
print_cpu_caps();
/* Exhaustive seek test. */
if (doTesting) {
printf("=======================================================================\n");
printf("DECODE TESTING\n");
printf("=======================================================================\n");
result = decode_test();
if (result != DRFLAC_SUCCESS) {
return (int)result; /* Don't continue if an error occurs during testing. */
}
printf("\n");
printf("=======================================================================\n");
printf("OPEN-AND-READ TESTING - drflac_open_*_and_read_pcm_frames_*()\n");
printf("=======================================================================\n");
result = open_and_read_test();
if (result != DRFLAC_SUCCESS) {
return (int)result; /* Don't continue if an error occurs during testing. */
}
printf("\n");
} else {
printf("=======================================================================\n");
printf("WARNING: Correctness Tests Disabled\n");
printf("=======================================================================\n");
}
/* Profiling. */
if (doProfiling) {
printf("=======================================================================\n");
printf("DECODE PROFILING (LOWER IS BETTER)\n");
printf("=======================================================================\n");
result = decode_profiling();
printf("\n");
}
/*getchar();*/
return (int)result;
}

View file

@ -0,0 +1 @@
#include "dr_flac_decoding.c"

View file

@ -0,0 +1,483 @@
/*#define DR_FLAC_NO_CRC*/
#include "dr_flac_common.c"
#define PROFILING_NAME_WIDTH 40
#define PROFILING_NUMBER_WIDTH 10
#define PROFILING_NUMBER_MARGIN 2
typedef struct
{
double totalSeconds_BruteForce;
double totalSeconds_BinarySearch;
double totalSeconds_SeekTable;
} profiling_state;
void profiling_state_init(profiling_state* pProfiling)
{
DRFLAC_ZERO_MEMORY(pProfiling, sizeof(*pProfiling));
}
profiling_state profiling_state_sum(const profiling_state* pA, const profiling_state* pB)
{
profiling_state result;
result.totalSeconds_BruteForce = pA->totalSeconds_BruteForce + pB->totalSeconds_BruteForce;
result.totalSeconds_BinarySearch = pA->totalSeconds_BinarySearch + pB->totalSeconds_BinarySearch;
result.totalSeconds_SeekTable = pA->totalSeconds_SeekTable + pB->totalSeconds_SeekTable;
return result;
}
drflac_result seek_test_pcm_frame(libflac* pLibFlac, drflac* pFlac, drflac_uint64 targetPCMFrameIndex)
{
drflac_bool32 seekResult;
drflac_uint64 pcmFrameCount_libflac;
drflac_uint64 pcmFrameCount_drflac;
drflac_int32* pPCMFrames_libflac;
drflac_int32* pPCMFrames_drflac;
drflac_uint64 iPCMFrame;
if (pFlac->_noSeekTableSeek == DRFLAC_FALSE && pFlac->_noBinarySearchSeek == DRFLAC_TRUE && pFlac->_noBruteForceSeek == DRFLAC_TRUE) {
if (pFlac->seekpointCount == 0) {
printf(" No seek table");
return DRFLAC_ERROR;
}
}
/*
To test seeking we just seek to the PCM frame, and then decode the rest of the file. If the PCM frames we read
differs between the two implementations there's something wrong with one of them (probably dr_flac).
*/
seekResult = libflac_seek_to_pcm_frame(pLibFlac, targetPCMFrameIndex);
if (seekResult == DRFLAC_FALSE) {
printf(" [libFLAC] Failed to seek to PCM frame @ %d", (int)targetPCMFrameIndex);
return DRFLAC_ERROR;
}
seekResult = drflac_seek_to_pcm_frame(pFlac, targetPCMFrameIndex);
if (seekResult == DRFLAC_FALSE) {
printf(" [dr_flac] Failed to seek to PCM frame @ %d", (int)targetPCMFrameIndex);
return DRFLAC_ERROR;
}
if (pLibFlac->currentPCMFrame != pFlac->currentPCMFrame) {
printf(" Current PCM frame inconsistent @ %d: libFLAC=%d, dr_flac=%d", (int)targetPCMFrameIndex, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
return DRFLAC_ERROR;
}
/*
Now we decode the rest of the file and compare the samples. Note that we try reading the _entire_ file, not just the leftovers, to ensure we
haven't seeked too short.
*/
pPCMFrames_libflac = (drflac_int32*)malloc((size_t)(pLibFlac->pcmFrameCount * pLibFlac->channels * sizeof(drflac_int32)));
if (pPCMFrames_libflac == NULL) {
printf(" [libFLAC] Out of memory");
return DRFLAC_ERROR;
}
pcmFrameCount_libflac = libflac_read_pcm_frames_s32(pLibFlac, pLibFlac->pcmFrameCount, pPCMFrames_libflac);
pPCMFrames_drflac = (drflac_int32*)malloc((size_t)(pLibFlac->pcmFrameCount * pLibFlac->channels * sizeof(drflac_int32)));
if (pPCMFrames_drflac == NULL) {
free(pPCMFrames_libflac);
printf(" [dr_flac] Out of memory");
return DRFLAC_ERROR;
}
pcmFrameCount_drflac = drflac_read_pcm_frames_s32(pFlac, pLibFlac->pcmFrameCount, pPCMFrames_drflac);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libflac != pcmFrameCount_drflac) {
free(pPCMFrames_drflac);
free(pPCMFrames_libflac);
printf(" Decoded frame counts differ @ %d: libFLAC=%d, dr_flac=%d", (int)targetPCMFrameIndex, (int)pLibFlac->currentPCMFrame, (int)pFlac->currentPCMFrame);
return DRFLAC_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRFLAC_ASSERT(pcmFrameCount_libflac == pcmFrameCount_drflac);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libflac; iPCMFrame += 1) {
drflac_int32* pPCMFrame_libflac = pPCMFrames_libflac + (iPCMFrame * pLibFlac->channels);
drflac_int32* pPCMFrame_drflac = pPCMFrames_drflac + (iPCMFrame * pLibFlac->channels);
drflac_uint32 iChannel;
drflac_bool32 hasError = DRFLAC_FALSE;
for (iChannel = 0; iChannel < pLibFlac->channels; iChannel += 1) {
if (pPCMFrame_libflac[iChannel] != pPCMFrame_drflac[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: targetPCMFrameIndex=%d", (int)iPCMFrame, iChannel, (int)targetPCMFrameIndex);
hasError = DRFLAC_TRUE;
break;
}
}
if (hasError) {
free(pPCMFrames_drflac);
free(pPCMFrames_libflac);
return DRFLAC_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
free(pPCMFrames_drflac);
free(pPCMFrames_libflac);
return DRFLAC_SUCCESS;
}
drflac_result seek_test_file(const char* pFilePath)
{
/* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libFLAC. */
drflac_result result;
libflac libflac;
drflac* pFlac;
drflac_uint32 iteration;
drflac_uint32 totalIterationCount = 10;
dr_printf_fixed_with_margin(PROFILING_NAME_WIDTH, PROFILING_NUMBER_MARGIN, "%s", dr_path_file_name(pFilePath));
/* First load the decoder from libFLAC. */
result = libflac_init_file(pFilePath, &libflac);
if (result != DRFLAC_SUCCESS) {
printf(" Failed to open via libFLAC.");
return result;
}
/* Now load from dr_flac. */
pFlac = drflac_open_file(pFilePath, NULL);
if (pFlac == NULL) {
printf(" Failed to open via dr_flac.");
libflac_uninit(&libflac);
return DRFLAC_ERROR; /* Failed to load dr_flac decoder. */
}
/* Use these to use specific seeking methods. Set all to false to use the normal prioritization (seek table, then binary search, then brute force). */
pFlac->_noSeekTableSeek = DRFLAC_FALSE;
pFlac->_noBinarySearchSeek = DRFLAC_FALSE;
pFlac->_noBruteForceSeek = DRFLAC_FALSE;
/* At this point we should have both libFLAC and dr_flac decoders open. We can now perform identical operations on each of them and compare. */
/* Start with the basics: Seek to the very end, and then the very start. */
if (result == DRFLAC_SUCCESS) {
result = seek_test_pcm_frame(&libflac, pFlac, libflac.pcmFrameCount);
}
if (result == DRFLAC_SUCCESS) {
result = seek_test_pcm_frame(&libflac, pFlac, 0);
}
/* Now we'll try seeking to random locations. */
dr_seed(1234);
iteration = 0;
while (result == DRFLAC_SUCCESS && iteration < totalIterationCount) {
dr_uint64 targetPCMFrame = dr_rand_range_u64(0, libflac.pcmFrameCount);
if (targetPCMFrame > libflac.pcmFrameCount) {
DRFLAC_ASSERT(DRFLAC_FALSE); /* Should never hit this, but if we do it means our random number generation routine is wrong. */
}
result = seek_test_pcm_frame(&libflac, pFlac, targetPCMFrame);
iteration += 1;
}
/* We're done with our decoders. */
drflac_close(pFlac);
libflac_uninit(&libflac);
if (result == DRFLAC_SUCCESS) {
printf(" Passed");
}
return result;
}
drflac_result seek_test_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
dr_printf_fixed(PROFILING_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "RESULT");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = seek_test_file(pFile->absolutePath);
(void)result;
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRFLAC_SUCCESS;
}
drflac_result seek_test()
{
drflac_result result = DRFLAC_SUCCESS;
/* Directories. */
{
result = seek_test_directory("testvectors/flac/tests");
(void)result;
}
return result;
}
drflac_result seek_profiling_drflac_and_close(drflac* pFlac, double* pProcessingTime)
{
drflac_result result = DRFLAC_SUCCESS;
int i;
if (pFlac == NULL) {
result = DRFLAC_INVALID_ARGS;
goto done;
}
if (pFlac->totalPCMFrameCount == 0) {
result = DRFLAC_INVALID_ARGS;
goto done;
}
if (pProcessingTime != NULL) {
*pProcessingTime = 0;
}
/* Seek back to the start to keep everything normalized. */
drflac_seek_to_pcm_frame(pFlac, 0);
/* Random seek points based on a seed. */
dr_seed(1234);
/*dr_seed(4321);*/
for (i = 0; i < 100; ++i) {
double startTime;
double endTime;
dr_uint64 targetPCMFrame = dr_rand_range_u64(0, pFlac->totalPCMFrameCount);
startTime = dr_timer_now();
{
drflac_seek_to_pcm_frame(pFlac, targetPCMFrame);
}
endTime = dr_timer_now();
if (pProcessingTime != NULL) {
*pProcessingTime += (endTime - startTime);
}
}
done:
drflac_close(pFlac);
return result;
}
drflac_result seek_profiling_file__seek_table(const char* pFilePath, double* pProcessingTime)
{
drflac* pFlac;
if (pFilePath == NULL) {
return DRFLAC_INVALID_ARGS;
}
pFlac = drflac_open_file(pFilePath, NULL);
if (pFlac == NULL) {
return DRFLAC_ERROR;
}
pFlac->_noSeekTableSeek = DRFLAC_FALSE;
pFlac->_noBinarySearchSeek = DRFLAC_TRUE;
pFlac->_noBruteForceSeek = DRFLAC_TRUE;
return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
}
drflac_result seek_profiling_file__binary_search(const char* pFilePath, double* pProcessingTime)
{
drflac* pFlac;
if (pFilePath == NULL) {
return DRFLAC_INVALID_ARGS;
}
pFlac = drflac_open_file(pFilePath, NULL);
if (pFlac == NULL) {
return DRFLAC_ERROR;
}
pFlac->_noSeekTableSeek = DRFLAC_TRUE;
pFlac->_noBinarySearchSeek = DRFLAC_FALSE;
pFlac->_noBruteForceSeek = DRFLAC_TRUE;
return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
}
drflac_result seek_profiling_file__brute_force(const char* pFilePath, double* pProcessingTime)
{
drflac* pFlac;
if (pFilePath == NULL) {
return DRFLAC_INVALID_ARGS;
}
pFlac = drflac_open_file(pFilePath, NULL);
if (pFlac == NULL) {
return DRFLAC_ERROR;
}
pFlac->_noSeekTableSeek = DRFLAC_TRUE;
pFlac->_noBinarySearchSeek = DRFLAC_TRUE;
pFlac->_noBruteForceSeek = DRFLAC_FALSE;
return seek_profiling_drflac_and_close(pFlac, pProcessingTime);
}
drflac_result seek_profiling_file(const char* pFilePath, profiling_state* pProfiling)
{
drflac_result result;
profiling_state_init(pProfiling);
/*
There are different seeking modes, and each one is profiled so that we can compare the results:
- Brute Force
- Binary Search
- Seek Table
In order to keep the total run time fair, we can only include files with a seek table.
*/
dr_printf_fixed_with_margin(PROFILING_NAME_WIDTH, 2, "%s", dr_path_file_name(pFilePath));
/* Start off with the seek table version. If this fails we don't bother continuing. */
#if 1
result = seek_profiling_file__seek_table(pFilePath, &pProfiling->totalSeconds_SeekTable);
if (result != DRFLAC_SUCCESS) {
return result;
}
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_SeekTable);
#else
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
#endif
#if 1
result = seek_profiling_file__binary_search(pFilePath, &pProfiling->totalSeconds_BinarySearch);
if (result != DRFLAC_SUCCESS) {
return result;
}
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_BinarySearch);
#else
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
#endif
#if 1
result = seek_profiling_file__brute_force(pFilePath, &pProfiling->totalSeconds_BruteForce);
if (result != DRFLAC_SUCCESS) {
return result;
}
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "%f", pProfiling->totalSeconds_BruteForce);
#else
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "");
#endif
return DRFLAC_SUCCESS;
}
drflac_result seek_profiling_directory(const char* pDirectoryPath, profiling_state* pProfiling)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
profiling_state_init(pProfiling);
dr_printf_fixed(PROFILING_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "S/Table");
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "Bin Srch");
dr_printf_fixed_with_margin(PROFILING_NUMBER_WIDTH, PROFILING_NUMBER_MARGIN, "B/Force");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drflac_result result;
profiling_state fileProfiling;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = seek_profiling_file(pFile->absolutePath, &fileProfiling);
if (result == DRFLAC_SUCCESS) {
*pProfiling = profiling_state_sum(pProfiling, &fileProfiling);
}
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRFLAC_SUCCESS;
}
drflac_result seek_profiling()
{
drflac_result result = DRFLAC_SUCCESS;
profiling_state globalProfiling;
profiling_state_init(&globalProfiling);
/* Directories. */
{
profiling_state directoryProfiling;
result = seek_profiling_directory("testvectors/flac/tests", &directoryProfiling);
if (result == DRFLAC_SUCCESS) {
globalProfiling = profiling_state_sum(&globalProfiling, &directoryProfiling);
}
}
return result;
}
int main(int argc, char** argv)
{
drflac_result result = DRFLAC_SUCCESS;
drflac_bool32 doTesting = DRFLAC_TRUE;
drflac_bool32 doProfiling = DRFLAC_TRUE;
/* This program has two main parts. The first is just a normal functionality test. The second is a profiling of the different seeking methods. */
if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
doTesting = DRFLAC_FALSE;
}
/* Exhaustive seek test. */
if (doTesting) {
printf("=======================================================================\n");
printf("SEEK TESTING\n");
printf("=======================================================================\n");
result = seek_test();
if (result != DRFLAC_SUCCESS) {
return (int)result; /* Don't continue if an error occurs during testing. */
}
printf("\n");
} else {
printf("=======================================================================\n");
printf("WARNING: Correctness Tests Disabled\n");
printf("=======================================================================\n");
}
/* Profiling. */
if (doProfiling) {
printf("=======================================================================\n");
printf("SEEK PROFILING\n");
printf("=======================================================================\n");
result = seek_profiling();
printf("\n");
}
/*getchar();*/
return (int)result;
}

View file

@ -0,0 +1,11 @@
#define DR_FLAC_IMPLEMENTATION
#include "../../dr_flac.h"
#include "../common/dr_common.c"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
return 0;
}

View file

@ -0,0 +1 @@
#include "dr_flac_test_0.c"

View file

@ -0,0 +1,76 @@
/*
* Fuzz tester for dr_flac.h
*
* compile with
* clang -g -O1 -fsanitize=fuzzer,address -o fuzz_dr_flac fuzz_dr_flac.c
*
* and run ./fuzz_dr_flac to run fuzz testing
*
* Other sanitizers are possible, for example
* -fsanitize=fuzzer,memory
* -fsanitize=fuzzer,undefined
*
* For more options, run ./fuzz_dr_flac -help=1
*
* If a problem is found, the problematic input is saved and can be
* rerun (with for example a debugger) with
*
* ./fuzz_dr_flac file
*
*/
#include <stdint.h>
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_CRC
#define DR_FLAC_NO_STDIO
#include "../../dr_flac.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
uint8_t fuzz_flacstream[4096] = {0};
size_t fuzz_flacstream_position;
size_t fuzz_flacstream_length;
static size_t read_fuzz_flacstream(void* pUserData, void* bufferOut, size_t bytesToRead)
{
size_t readsize = MIN(bytesToRead, fuzz_flacstream_length-fuzz_flacstream_position);
if (readsize > 0) {
memcpy(bufferOut, fuzz_flacstream+fuzz_flacstream_position, readsize);
fuzz_flacstream_position += readsize;
return readsize;
} else {
return 0;
}
}
static drflac_bool32 seek_fuzz_flacstream(void* pUserData, int offset, drflac_seek_origin origin)
{
if ((int)fuzz_flacstream_position+offset < 0 || (int)fuzz_flacstream_position+offset > fuzz_flacstream_length) {
return 1;
} else {
fuzz_flacstream_position = (int)fuzz_flacstream_position+offset;
return 0;
}
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
if (size > 2) {
drflac * drflac_fuzzer;
drflac_int32 drflac_fuzzer_out[2048] = {0}; /* 256 samples over 8 channels */
drflac_container container = data[0] & 1 ? drflac_container_native : drflac_container_ogg;
memcpy(fuzz_flacstream, data, (size-1)<4096?(size-1):4096);
fuzz_flacstream_position = 0;
fuzz_flacstream_length = size-1;
drflac_fuzzer = drflac_open_relaxed(read_fuzz_flacstream, seek_fuzz_flacstream, container, NULL, NULL);
while (drflac_read_pcm_frames_s32(drflac_fuzzer, 256, drflac_fuzzer_out));
drflac_close(drflac_fuzzer);
}
return 0;
}

View file

@ -0,0 +1,4 @@
#define DR_MP3_IMPLEMENTATION
#include "../../dr_mp3.h"
#include "../common/dr_common.c"

View file

@ -0,0 +1,161 @@
#include "dr_mp3_common.c"
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
#include "../external/miniaudio/miniaudio.h"
void data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
{
drmp3* pMP3;
pMP3 = (drmp3*)pDevice->pUserData;
DRMP3_ASSERT(pMP3 != NULL);
if (pDevice->playback.format == ma_format_f32) {
drmp3_read_pcm_frames_f32(pMP3, frameCount, pFramesOut);
} else if (pDevice->playback.format == ma_format_s16) {
drmp3_read_pcm_frames_s16(pMP3, frameCount, pFramesOut);
} else {
DRMP3_ASSERT(DRMP3_FALSE); /* Should never get here. */
}
(void)pFramesIn;
}
int do_decoding_validation(const char* pFilePath)
{
int result = 0;
drmp3 mp3Memory;
drmp3 mp3File;
size_t dataSize;
void* pData;
/*
When opening from a memory buffer, dr_mp3 will take a different path for decoding which is optimized to reduce data movement. Since it's
running on a separate path, we need to ensure it's returning consistent results with the other code path which will be used when decoding
from a file.
*/
/* Initialize the memory decoder. */
pData = dr_open_and_read_file(pFilePath, &dataSize);
if (pData == NULL) {
printf("Failed to open file \"%s\"", pFilePath);
return -1;
}
if (!drmp3_init_memory(&mp3Memory, pData, dataSize, NULL)) {
free(pData);
printf("Failed to init MP3 decoder \"%s\"", pFilePath);
return -1;
}
/* Initialize the file decoder. */
if (!drmp3_init_file(&mp3File, pFilePath, NULL)) {
drmp3_uninit(&mp3Memory);
free(pData);
printf("Failed to open file \"%s\"", pFilePath);
return -1;
}
DRMP3_ASSERT(mp3Memory.channels == mp3File.channels);
/* Compare. */
for (;;) {
drmp3_uint64 iSample;
drmp3_uint64 pcmFrameCountMemory;
drmp3_uint64 pcmFrameCountFile;
drmp3_int16 pcmFramesMemory[4096];
drmp3_int16 pcmFramesFile[4096];
pcmFrameCountMemory = drmp3_read_pcm_frames_s16(&mp3Memory, DRMP3_COUNTOF(pcmFramesMemory) / mp3Memory.channels, pcmFramesMemory);
pcmFrameCountFile = drmp3_read_pcm_frames_s16(&mp3File, DRMP3_COUNTOF(pcmFramesFile) / mp3File.channels, pcmFramesFile);
/* Check the frame count first. */
if (pcmFrameCountMemory != pcmFrameCountFile) {
printf("Frame counts differ: memory = %d; file = %d\n", (int)pcmFrameCountMemory, (int)pcmFrameCountFile);
result = -1;
break;
}
/* Check individual frames. */
DRMP3_ASSERT(pcmFrameCountMemory == pcmFrameCountFile);
for (iSample = 0; iSample < pcmFrameCountMemory * mp3Memory.channels; iSample += 1) {
if (pcmFramesMemory[iSample] != pcmFramesFile[iSample]) {
printf("Samples differ: memory = %d; file = %d\n", (int)pcmFramesMemory[iSample], (int)pcmFramesFile[iSample]);
result = -1;
break;
}
}
/* We've reached the end if we didn't return any PCM frames. */
if (pcmFrameCountMemory == 0 || pcmFrameCountFile == 0) {
break;
}
}
drmp3_uninit(&mp3File);
drmp3_uninit(&mp3Memory);
free(pData);
return result;
}
int main(int argc, char** argv)
{
drmp3 mp3;
ma_result resultMA;
ma_device_config deviceConfig;
ma_device device;
const char* pInputFilePath;
if (argc < 2) {
printf("No input file.\n");
return -1;
}
pInputFilePath = argv[1];
/* Quick validation test first. */
if (do_decoding_validation(pInputFilePath) != 0) {
return -1;
}
if (!drmp3_init_file(&mp3, pInputFilePath, NULL)) {
printf("Failed to open file \"%s\"", pInputFilePath);
return -1;
}
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = ma_format_s16;
deviceConfig.playback.channels = mp3.channels;
deviceConfig.sampleRate = mp3.sampleRate;
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = &mp3;
resultMA = ma_device_init(NULL, &deviceConfig, &device);
if (resultMA != MA_SUCCESS) {
drmp3_uninit(&mp3);
printf("Failed to initialize playback device: %s.\n", ma_result_description(resultMA));
return -1;
}
resultMA = ma_device_start(&device);
if (resultMA != MA_SUCCESS) {
ma_device_uninit(&device);
drmp3_uninit(&mp3);
printf("Failed to start playback device: %s.\n", ma_result_description(resultMA));
return -1;
}
printf("Press Enter to quit...");
getchar();
/* We're done. */
ma_device_uninit(&device);
drmp3_uninit(&mp3);
return 0;
}

View file

@ -0,0 +1,12 @@
#define DR_MP3_IMPLEMENTATION
#include "../../dr_mp3.h"
#include "../common/dr_common.c"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
return 0;
}

View file

@ -0,0 +1,205 @@
/*#define DR_OPUS_DEBUGGING*/
#define DR_OPUS_IMPLEMENTATION
#include "../../wip/dr_opus.h"
#include "../common/dr_common.c"
const char* dropus_mode_to_string(dropus_mode mode) /* Move this into dr_opus.h? */
{
switch (mode)
{
case dropus_mode_silk: return "SILK";
case dropus_mode_celt: return "CELT";
case dropus_mode_hybrid: return "Hybrid";
default: break;
}
return "Unknown";
}
#include <stdio.h>
/* Forward declare our debugging entry point if necessary. */
#ifdef DR_OPUS_DEBUGGING
int main_debugging(int argc, char** argv);
#endif
dropus_result test_standard_vector(const char* pFilePath)
{
dropus_result result;
dropus_stream stream;
void* pFileData;
size_t fileSize;
const dropus_uint8* pRunningData8;
size_t runningPos;
dropus_uint32 iPacket = 0; /* For error reporting. */
/* Load the entire file into memory first. */
pFileData = dr_open_and_read_file(pFilePath, &fileSize);
if (pFileData == NULL) {
return DROPUS_ERROR; /* File not found. */
}
/* Now initialize the stream in preparation for decoding packets. */
result = dropus_stream_init(&stream);
if (result != DROPUS_SUCCESS) {
free(pFileData);
return result;
}
/*
Here is where we scan the test vector. The way the file is structured is quite simple. It is made up of a list of Opus packets, each
of which include an 8 byte header where the first 4 bytes is the size in bytes of the Opus packet (little endian) and the next 4 bytes
contain the 32-bit range state which we'll use for validation.
*/
pRunningData8 = (const dropus_uint8*)pFileData;
runningPos = 0;
/* For each packet... */
while (runningPos < fileSize) {
dropus_result decodeResult;
dropus_uint32 packetSize;
dropus_uint32 rangeState;
memcpy(&packetSize, pRunningData8 + 0, 4);
memcpy(&rangeState, pRunningData8 + 4, 4);
packetSize = dropus__be2host_32(packetSize);
rangeState = dropus__be2host_32(rangeState);
pRunningData8 += 8;
runningPos += 8;
/* Safety. Break if we've run out of data. */
if ((runningPos + packetSize) > fileSize) {
printf("WARNING: Ran out of data before the end of the file.\n");
break;
}
decodeResult = dropus_stream_decode_packet(&stream, pRunningData8, packetSize);
if (decodeResult != DROPUS_SUCCESS) {
result = DROPUS_ERROR;
printf("Failed to decode packet %d\n", iPacket);
}
printf("Opus Packet %d: Mode=%s\n", iPacket, dropus_mode_to_string(dropus_toc_mode(stream.packet.toc)));
pRunningData8 += packetSize;
runningPos += packetSize;
iPacket += 1;
}
free(pFileData);
return result;
}
dropus_result test_standard_vectors_folder(const char* pFolderPath)
{
dropus_bool32 foundError = DROPUS_FALSE;
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
pFile = dr_file_iterator_begin(pFolderPath, &iteratorState);
while (pFile != NULL) {
/* Only look at files with the extension "bit". */
if (dr_extension_equal(pFile->relativePath, "bit")) {
if (test_standard_vector(pFile->absolutePath) != DROPUS_SUCCESS) {
foundError = DROPUS_TRUE;
}
}
pFile = dr_file_iterator_next(pFile);
}
if (foundError) {
return DROPUS_ERROR;
} else {
return DROPUS_SUCCESS;
}
}
dropus_result test_standard_vectors()
{
dropus_bool32 foundError = DROPUS_FALSE;
/* Two groups of standard test vectors. The original vectors and the "new" vectors. */
/* Original vectors. */
if (test_standard_vectors_folder("testvectors/opus/opus_testvectors") != DROPUS_SUCCESS) {
foundError = DROPUS_TRUE;
}
/* New vectors. */
if (test_standard_vectors_folder("testvectors/opus/opus_newvectors") != DROPUS_SUCCESS) {
foundError = DROPUS_TRUE;
}
if (foundError) {
return DROPUS_ERROR;
} else {
return DROPUS_SUCCESS;
}
}
dropus_result test_ogg_vectors()
{
dropus_result result;
/* Ogg Opus test vectors are in the "oggopus" folder. */
result = DROPUS_SUCCESS;
return result;
}
int main(int argc, char** argv)
{
dropus_result result;
/* Do debugging stuff first. */
#ifdef DR_OPUS_DEBUGGING
main_debugging(argc, argv);
#endif
result = test_standard_vector("testvectors/opus/opus_testvectors/testvector02.bit");
if (result != DROPUS_SUCCESS) {
/*return (int)result;*/
}
/* Test standard vectors first. */
result = test_standard_vectors();
if (result != DROPUS_SUCCESS) {
/*return (int)result;*/
}
/* Ogg Opus. */
result = test_ogg_vectors();
if (result != DROPUS_SUCCESS) {
/*return (int)result;*/
}
(void)argc;
(void)argv;
return 0;
}
#ifdef DR_OPUS_DEBUGGING
int main_debugging(int argc, char** argv)
{
dr_file_iterator iterator;
dr_file_iterator* pFile = dr_file_iterator_begin("testvectors/opus/oggopus", &iterator);
while (pFile != NULL) {
if (pFile->isDirectory) {
printf("DIRECTORY: %s : %s\n", pFile->relativePath, pFile->absolutePath);
} else {
printf("FILE: %s : %s\n", pFile->relativePath, pFile->absolutePath);
}
pFile = dr_file_iterator_next(pFile);
}
(void)argc;
(void)argv;
return 0;
}
#endif

View file

@ -0,0 +1,9 @@
#define DR_OPUS_IMPLEMENTATION
#include "../../wip/dr_opus.h"
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
return 0;
}

View file

@ -0,0 +1 @@
#include "dr_opus_test_0.c"

View file

@ -0,0 +1,326 @@
#define DR_WAV_IMPLEMENTATION
#include "../../dr_wav.h"
#include <sndfile.h>
#include "../common/dr_common.c"
dr_handle g_libsndfile = NULL;
typedef SNDFILE* (* pfn_sf_open_virtual)(SF_VIRTUAL_IO* sfvirtual, int mode, SF_INFO* sfinfo, void* user_data);
typedef int (* pfn_sf_close) (SNDFILE* sndfile);
typedef sf_count_t (* pfn_sf_readf_short) (SNDFILE *sndfile, short *ptr, sf_count_t frames);
typedef sf_count_t (* pfn_sf_readf_int) (SNDFILE *sndfile, int *ptr, sf_count_t frames);
typedef sf_count_t (* pfn_sf_readf_float) (SNDFILE *sndfile, float *ptr, sf_count_t frames);
typedef sf_count_t (* pfn_sf_readf_double)(SNDFILE *sndfile, double *ptr, sf_count_t frames);
typedef sf_count_t (* pfn_sf_seek) (SNDFILE *sndfile, sf_count_t frames, int whence);
pfn_sf_open_virtual libsndfile__sf_open_virtual;
pfn_sf_close libsndfile__sf_close;
pfn_sf_readf_short libsndfile__sf_readf_short;
pfn_sf_readf_int libsndfile__sf_readf_int;
pfn_sf_readf_float libsndfile__sf_readf_float;
pfn_sf_readf_double libsndfile__sf_readf_double;
pfn_sf_seek libsndfile__sf_seek;
drwav_result libsndfile_init_api()
{
unsigned int i;
const char* pFileNames[] = {
#if defined(_WIN32)
#if defined(_WIN64)
"libsndfile-1-x64.dll",
#else
"libsndfile-1-x86.dll",
#endif
"libsndfile-1.dll"
#else
"libsndfile-1.so",
"libsndfile.so.1"
#endif
};
if (g_libsndfile != NULL) {
return DRWAV_INVALID_OPERATION; /* Already initialized. */
}
for (i = 0; i < sizeof(pFileNames)/sizeof(pFileNames[0]); i += 1) {
g_libsndfile = dr_dlopen(pFileNames[i]);
if (g_libsndfile != NULL) {
break;
}
}
if (g_libsndfile == NULL) {
return DRWAV_ERROR; /* Unable to load libsndfile-1.so/dll. */
}
libsndfile__sf_open_virtual = (pfn_sf_open_virtual)dr_dlsym(g_libsndfile, "sf_open_virtual");
libsndfile__sf_close = (pfn_sf_close) dr_dlsym(g_libsndfile, "sf_close");
libsndfile__sf_readf_short = (pfn_sf_readf_short) dr_dlsym(g_libsndfile, "sf_readf_short");
libsndfile__sf_readf_int = (pfn_sf_readf_int) dr_dlsym(g_libsndfile, "sf_readf_int");
libsndfile__sf_readf_float = (pfn_sf_readf_float) dr_dlsym(g_libsndfile, "sf_readf_float");
libsndfile__sf_readf_double = (pfn_sf_readf_double)dr_dlsym(g_libsndfile, "sf_readf_double");
libsndfile__sf_seek = (pfn_sf_seek) dr_dlsym(g_libsndfile, "sf_seek");
return DRWAV_SUCCESS;
}
void libsndfile_uninit_api()
{
if (g_libsndfile == NULL) {
return; /* Invalid operation. Not initialized. */
}
dr_dlclose(g_libsndfile);
g_libsndfile = NULL;
}
typedef struct
{
SNDFILE* pHandle;
SF_INFO info;
drwav_uint8* pFileData;
size_t fileSizeInBytes;
size_t fileReadPos;
} libsndfile;
sf_count_t libsndfile__on_filelen(void *user_data)
{
libsndfile* pSndFile = (libsndfile*)user_data;
return (sf_count_t)pSndFile->fileSizeInBytes;
}
sf_count_t libsndfile__on_seek(sf_count_t offset, int whence, void *user_data)
{
libsndfile* pSndFile = (libsndfile*)user_data;
switch (whence)
{
case SF_SEEK_SET:
{
pSndFile->fileReadPos = (size_t)offset;
} break;
case SF_SEEK_CUR:
{
pSndFile->fileReadPos += (size_t)offset;
} break;
case SF_SEEK_END:
{
pSndFile->fileReadPos = pSndFile->fileSizeInBytes - (size_t)offset;
} break;
}
return (sf_count_t)pSndFile->fileReadPos;
}
sf_count_t libsndfile__on_read(void *ptr, sf_count_t count, void *user_data)
{
libsndfile* pSndFile = (libsndfile*)user_data;
DRWAV_COPY_MEMORY(ptr, pSndFile->pFileData + pSndFile->fileReadPos, (size_t)count);
pSndFile->fileReadPos += (size_t)count;
return count;
}
sf_count_t libsndfile__on_write(const void *ptr, sf_count_t count, void *user_data)
{
/* We're not doing anything with writing. */
(void)ptr;
(void)count;
(void)user_data;
return 0;
}
sf_count_t libsndfile__on_tell(void *user_data)
{
libsndfile* pSndFile = (libsndfile*)user_data;
return (sf_count_t)pSndFile->fileReadPos;
}
drwav_result libsndfile_init_file(const char* pFilePath, libsndfile* pSndFile)
{
SF_VIRTUAL_IO callbacks;
if (pFilePath == NULL || pSndFile == NULL) {
return DRWAV_INVALID_ARGS;
}
DRWAV_ZERO_MEMORY(pSndFile, sizeof(*pSndFile));
/* We use libsndfile's virtual IO technique because we want to load from memory to make speed benchmarking fairer. */
pSndFile->pFileData = (drwav_uint8*)dr_open_and_read_file(pFilePath, &pSndFile->fileSizeInBytes);
if (pSndFile->pFileData == NULL) {
return DRWAV_ERROR; /* Failed to open the file. */
}
DRWAV_ZERO_MEMORY(&callbacks, sizeof(callbacks));
callbacks.get_filelen = libsndfile__on_filelen;
callbacks.seek = libsndfile__on_seek;
callbacks.read = libsndfile__on_read;
callbacks.write = libsndfile__on_write;
callbacks.tell = libsndfile__on_tell;
pSndFile->pHandle = libsndfile__sf_open_virtual(&callbacks, SFM_READ, &pSndFile->info, pSndFile);
if (pSndFile->pHandle == NULL) {
free(pSndFile->pFileData);
return DRWAV_ERROR;
}
return DRWAV_SUCCESS;
}
void libsndfile_uninit(libsndfile* pSndFile)
{
if (pSndFile == NULL) {
return;
}
libsndfile__sf_close(pSndFile->pHandle);
free(pSndFile->pFileData);
}
drwav_uint64 libsndfile_read_pcm_frames_s16(libsndfile* pSndFile, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
{
if (pSndFile == NULL || pBufferOut == NULL) {
return 0;
}
/* Unfortunately it looks like libsndfile does not return correct integral values when the source file is floating point. */
if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
/* Read as float and convert. */
drwav_uint64 totalFramesRead = 0;
while (totalFramesRead < framesToRead) {
float temp[4096];
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
drwav_uint64 framesReadThisIteration;
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
if (framesToReadThisIteration > framesRemaining) {
framesToReadThisIteration = framesRemaining;
}
framesReadThisIteration = libsndfile__sf_readf_float(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
drwav_f32_to_s16(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
totalFramesRead += framesReadThisIteration;
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
/* If we read less frames than we requested we've reached the end of the file. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return totalFramesRead;
} else if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) {
/* Read as double and convert. */
drwav_uint64 totalFramesRead = 0;
while (totalFramesRead < framesToRead) {
double temp[4096];
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
drwav_uint64 framesReadThisIteration;
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
if (framesToReadThisIteration > framesRemaining) {
framesToReadThisIteration = framesRemaining;
}
framesReadThisIteration = libsndfile__sf_readf_double(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
drwav_f64_to_s16(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
totalFramesRead += framesReadThisIteration;
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
/* If we read less frames than we requested we've reached the end of the file. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return totalFramesRead;
} else {
return libsndfile__sf_readf_short(pSndFile->pHandle, pBufferOut, framesToRead);
}
}
drwav_uint64 libsndfile_read_pcm_frames_f32(libsndfile* pSndFile, drwav_uint64 framesToRead, float* pBufferOut)
{
if (pSndFile == NULL || pBufferOut == NULL) {
return 0;
}
return libsndfile__sf_readf_float(pSndFile->pHandle, pBufferOut, framesToRead);
}
drwav_uint64 libsndfile_read_pcm_frames_s32(libsndfile* pSndFile, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
{
if (pSndFile == NULL || pBufferOut == NULL) {
return 0;
}
/* Unfortunately it looks like libsndfile does not return correct integral values when the source file is floating point. */
if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
/* Read floats and convert. */
drwav_uint64 totalFramesRead = 0;
while (totalFramesRead < framesToRead) {
float temp[4096];
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
drwav_uint64 framesReadThisIteration;
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
if (framesToReadThisIteration > framesRemaining) {
framesToReadThisIteration = framesRemaining;
}
framesReadThisIteration = libsndfile__sf_readf_float(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
drwav_f32_to_s32(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
totalFramesRead += framesReadThisIteration;
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
/* If we read less frames than we requested we've reached the end of the file. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return totalFramesRead;
} else if ((pSndFile->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) {
/* Read doubles and convert. */
drwav_uint64 totalFramesRead = 0;
while (totalFramesRead < framesToRead) {
double temp[4096];
drwav_uint64 framesRemaining = framesToRead - totalFramesRead;
drwav_uint64 framesReadThisIteration;
drwav_uint64 framesToReadThisIteration = sizeof(temp)/sizeof(temp[0]) / pSndFile->info.channels;
if (framesToReadThisIteration > framesRemaining) {
framesToReadThisIteration = framesRemaining;
}
framesReadThisIteration = libsndfile__sf_readf_double(pSndFile->pHandle, temp, (sf_count_t)framesToReadThisIteration);
drwav_f64_to_s32(pBufferOut, temp, (size_t)(framesReadThisIteration*pSndFile->info.channels));
totalFramesRead += framesReadThisIteration;
pBufferOut += framesReadThisIteration*pSndFile->info.channels;
/* If we read less frames than we requested we've reached the end of the file. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return totalFramesRead;
} else {
return libsndfile__sf_readf_int(pSndFile->pHandle, pBufferOut, framesToRead);
}
}
drwav_bool32 libsndfile_seek_to_pcm_frame(libsndfile* pSndFile, drwav_uint64 targetPCMFrameIndex)
{
if (pSndFile == NULL) {
return DRWAV_FALSE;
}
return libsndfile__sf_seek(pSndFile->pHandle, (sf_count_t)targetPCMFrameIndex, SF_SEEK_SET) == (sf_count_t)targetPCMFrameIndex;
}

View file

@ -0,0 +1,446 @@
#define DR_WAV_LIBSNDFILE_COMPAT
#include "dr_wav_common.c"
#define FILE_NAME_WIDTH 40
#define NUMBER_WIDTH 10
#define TABLE_MARGIN 2
#define DEFAULT_SOURCE_DIR "testvectors/wav/tests"
drwav_result decode_test__read_and_compare_pcm_frames_s32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, drwav_int32* pPCMFrames_libsndfile, drwav_int32* pPCMFrames_drwav)
{
drwav_uint64 pcmFrameCount_libsndfile;
drwav_uint64 pcmFrameCount_drwav;
drwav_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_s32(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
pcmFrameCount_drwav = drwav_read_pcm_frames_s32(pWav, pcmFrameCount, pPCMFrames_drwav);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
return DRWAV_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
drwav_int32* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
drwav_int32* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
drwav_uint32 iChannel;
drwav_bool32 hasError = DRWAV_FALSE;
for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRWAV_TRUE;
break;
}
}
if (hasError) {
return DRWAV_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRWAV_SUCCESS;
}
drwav_result decode_test__read_and_compare_pcm_frame_chunks_s32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
{
drwav_result result = DRWAV_SUCCESS;
drwav_uint64 iPCMFrame;
drwav_int32* pPCMFrames_libsndfile;
drwav_int32* pPCMFrames_drwav;
/* Make sure the decoder's are seeked back to the start first. */
drwav_seek_to_pcm_frame(pWav, 0);
libsndfile_seek_to_pcm_frame(pSndFile, 0);
pPCMFrames_libsndfile = (drwav_int32*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int32)));
if (pPCMFrames_libsndfile == NULL) {
printf(" [libsndfile] Out of memory");
return DRWAV_ERROR;
}
pPCMFrames_drwav = (drwav_int32*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int32)));
if (pPCMFrames_drwav == NULL) {
free(pPCMFrames_libsndfile);
printf(" [dr_wav] Out of memory");
return DRWAV_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_s32(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
if (result != DRWAV_SUCCESS) {
break;
}
}
free(pPCMFrames_libsndfile);
free(pPCMFrames_drwav);
return result;
}
drwav_result decode_test__read_and_compare_pcm_frames_f32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, float* pPCMFrames_libsndfile, float* pPCMFrames_drwav)
{
drwav_uint64 pcmFrameCount_libsndfile;
drwav_uint64 pcmFrameCount_drwav;
drwav_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_f32(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
pcmFrameCount_drwav = drwav_read_pcm_frames_f32(pWav, pcmFrameCount, pPCMFrames_drwav);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
return DRWAV_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
float* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
float* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
drwav_uint32 iChannel;
drwav_bool32 hasError = DRWAV_FALSE;
for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRWAV_TRUE;
break;
}
}
if (hasError) {
return DRWAV_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRWAV_SUCCESS;
}
drwav_result decode_test__read_and_compare_pcm_frame_chunks_f32(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
{
drwav_result result = DRWAV_SUCCESS;
drwav_uint64 iPCMFrame;
float* pPCMFrames_libsndfile;
float* pPCMFrames_drwav;
/* Make sure the decoder's are seeked back to the start first. */
drwav_seek_to_pcm_frame(pWav, 0);
libsndfile_seek_to_pcm_frame(pSndFile, 0);
pPCMFrames_libsndfile = (float*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(float)));
if (pPCMFrames_libsndfile == NULL) {
printf(" [libsndfile] Out of memory");
return DRWAV_ERROR;
}
pPCMFrames_drwav = (float*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(float)));
if (pPCMFrames_drwav == NULL) {
free(pPCMFrames_libsndfile);
printf(" [dr_wav] Out of memory");
return DRWAV_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_f32(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
if (result != DRWAV_SUCCESS) {
break;
}
}
free(pPCMFrames_libsndfile);
free(pPCMFrames_drwav);
return result;
}
drwav_result decode_test__read_and_compare_pcm_frames_s16(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameCount, drwav_int16* pPCMFrames_libsndfile, drwav_int16* pPCMFrames_drwav)
{
drwav_uint64 pcmFrameCount_libsndfile;
drwav_uint64 pcmFrameCount_drwav;
drwav_uint64 iPCMFrame;
/* To test decoding we just read a number of PCM frames from each decoder and compare. */
pcmFrameCount_libsndfile = libsndfile_read_pcm_frames_s16(pSndFile, pcmFrameCount, pPCMFrames_libsndfile);
pcmFrameCount_drwav = drwav_read_pcm_frames_s16(pWav, pcmFrameCount, pPCMFrames_drwav);
/* The total number of frames we decoded need to match. */
if (pcmFrameCount_libsndfile != pcmFrameCount_drwav) {
printf(" Decoded frame counts differ: pcmFrameCount=%d, libsndfile=%d, dr_wav=%d", (int)pcmFrameCount, (int)pcmFrameCount_libsndfile, (int)pcmFrameCount_drwav);
return DRWAV_ERROR;
}
/* Each of the decoded PCM frames need to match. */
DRWAV_ASSERT(pcmFrameCount_libsndfile == pcmFrameCount_drwav);
for (iPCMFrame = 0; iPCMFrame < pcmFrameCount_libsndfile; iPCMFrame += 1) {
drwav_int16* pPCMFrame_libsndfile = pPCMFrames_libsndfile + (iPCMFrame * pWav->channels);
drwav_int16* pPCMFrame_drwav = pPCMFrames_drwav + (iPCMFrame * pWav->channels);
drwav_uint32 iChannel;
drwav_bool32 hasError = DRWAV_FALSE;
for (iChannel = 0; iChannel < pWav->channels; iChannel += 1) {
if (pPCMFrame_libsndfile[iChannel] != pPCMFrame_drwav[iChannel]) {
printf(" PCM Frame @ %d[%d] does not match: pcmFrameCount=%d", (int)iPCMFrame, iChannel, (int)pcmFrameCount);
hasError = DRWAV_TRUE;
break;
}
}
if (hasError) {
return DRWAV_ERROR; /* Decoded frames do not match. */
}
}
/* Done. */
return DRWAV_SUCCESS;
}
drwav_result decode_test__read_and_compare_pcm_frame_chunks_s16(libsndfile* pSndFile, drwav* pWav, drwav_uint64 pcmFrameChunkSize)
{
drwav_result result = DRWAV_SUCCESS;
drwav_uint64 iPCMFrame;
drwav_int16* pPCMFrames_libsndfile;
drwav_int16* pPCMFrames_drwav;
/* Make sure the decoder's are seeked back to the start first. */
drwav_seek_to_pcm_frame(pWav, 0);
libsndfile_seek_to_pcm_frame(pSndFile, 0);
pPCMFrames_libsndfile = (drwav_int16*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int16)));
if (pPCMFrames_libsndfile == NULL) {
printf(" [libsndfile] Out of memory");
return DRWAV_ERROR;
}
pPCMFrames_drwav = (drwav_int16*)malloc((size_t)(pcmFrameChunkSize * pWav->channels * sizeof(drwav_int16)));
if (pPCMFrames_drwav == NULL) {
free(pPCMFrames_libsndfile);
printf(" [dr_wav] Out of memory");
return DRWAV_ERROR;
}
for (iPCMFrame = 0; iPCMFrame < pWav->totalPCMFrameCount; iPCMFrame += pcmFrameChunkSize) {
result = decode_test__read_and_compare_pcm_frames_s16(pSndFile, pWav, pcmFrameChunkSize, pPCMFrames_libsndfile, pPCMFrames_drwav);
if (result != DRWAV_SUCCESS) {
break;
}
}
free(pPCMFrames_libsndfile);
free(pPCMFrames_drwav);
return result;
}
drwav_result decode_test_file_s32(libsndfile* pSndFile, drwav* pWav)
{
drwav_result result = DRWAV_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pSndFile, pWav, pWav->totalPCMFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s32(pSndFile, pWav, 1);
}
return result;
}
drwav_result decode_test_file_f32(libsndfile* pSndFile, drwav* pWav)
{
drwav_result result = DRWAV_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pSndFile, pWav, pWav->totalPCMFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_f32(pSndFile, pWav, 1);
}
return result;
}
drwav_result decode_test_file_s16(libsndfile* pSndFile, drwav* pWav)
{
drwav_result result = DRWAV_SUCCESS;
/* Start with reading the entire file in one go. */
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pSndFile, pWav, pWav->totalPCMFrameCount);
}
/* Now try with reading one PCM frame at a time.*/
if (result == DRWAV_SUCCESS) {
result = decode_test__read_and_compare_pcm_frame_chunks_s16(pSndFile, pWav, 1);
}
return result;
}
drwav_result decode_test_file(const char* pFilePath)
{
/* To test seeking we just seek to our target PCM frame and then decode whatever is remaining and compare it against libsndfile. */
drwav_result result;
libsndfile libsndfile;
drwav wav;
dr_printf_fixed_with_margin(FILE_NAME_WIDTH, TABLE_MARGIN, "%s", dr_path_file_name(pFilePath));
/* First load the decoder from libsndfile. */
result = libsndfile_init_file(pFilePath, &libsndfile);
if (result != DRWAV_SUCCESS) {
printf(" Failed to open via libsndfile.");
return result;
}
/* Now load from dr_wav. */
if (!drwav_init_file_with_metadata(&wav, pFilePath, 0, NULL)) {
printf(" Failed to open via dr_wav.");
libsndfile_uninit(&libsndfile);
return DRWAV_ERROR; /* Failed to load dr_wav decoder. */
}
/* At this point we should have both libsndfile and dr_wav decoders open. We can now perform identical operations on each of them and compare. */
result = decode_test_file_s32(&libsndfile, &wav);
if (result != DRWAV_SUCCESS) {
drwav_uninit(&wav);
libsndfile_uninit(&libsndfile);
return result;
}
result = decode_test_file_f32(&libsndfile, &wav);
if (result != DRWAV_SUCCESS) {
drwav_uninit(&wav);
libsndfile_uninit(&libsndfile);
return result;
}
result = decode_test_file_s16(&libsndfile, &wav);
if (result != DRWAV_SUCCESS) {
drwav_uninit(&wav);
libsndfile_uninit(&libsndfile);
return result;
}
/* We're done with our decoders. */
drwav_uninit(&wav);
libsndfile_uninit(&libsndfile);
if (result == DRWAV_SUCCESS) {
printf(" Passed");
}
return result;
}
drwav_result decode_test_directory(const char* pDirectoryPath)
{
dr_file_iterator iteratorState;
dr_file_iterator* pFile;
dr_printf_fixed(FILE_NAME_WIDTH, "%s", pDirectoryPath);
dr_printf_fixed_with_margin(NUMBER_WIDTH, TABLE_MARGIN, "RESULT");
printf("\n");
pFile = dr_file_iterator_begin(pDirectoryPath, &iteratorState);
while (pFile != NULL) {
drwav_result result;
/* Skip directories for now, but we may want to look at doing recursive file iteration. */
if (!pFile->isDirectory) {
result = decode_test_file(pFile->absolutePath);
(void)result;
printf("\n");
}
pFile = dr_file_iterator_next(pFile);
}
return DRWAV_SUCCESS;
}
drwav_result decode_test()
{
drwav_result result = DRWAV_SUCCESS;
/* Directories. */
{
result = decode_test_directory(DEFAULT_SOURCE_DIR);
(void)result;
}
return result;
}
drwav_result decode_profiling()
{
return DRWAV_SUCCESS;
}
int main(int argc, char** argv)
{
drwav_result result = DRWAV_SUCCESS;
drwav_bool32 doTesting = DRWAV_TRUE;
drwav_bool32 doProfiling = DRWAV_TRUE;
if (dr_argv_is_set(argc, argv, "--onlyprofile")) {
doTesting = DRWAV_FALSE;
}
if (libsndfile_init_api() != DRWAV_SUCCESS) {
printf("Failed to initialize libsndfile API.");
return -1;
}
if (doTesting) {
printf("=======================================================================\n");
printf("DECODE TESTING\n");
printf("=======================================================================\n");
result = decode_test();
if (result != DRWAV_SUCCESS) {
goto done; /* Don't continue if an error occurs during testing. */
}
printf("\n");
} else {
printf("=======================================================================\n");
printf("WARNING: Correctness Tests Disabled\n");
printf("=======================================================================\n");
}
/* Profiling. */
if (doProfiling) {
printf("=======================================================================\n");
printf("DECODE PROFILING (LOWER IS BETTER)\n");
printf("=======================================================================\n");
result = decode_profiling();
printf("\n");
}
done:
libsndfile_uninit_api();
return (int)result;
}

View file

@ -0,0 +1 @@
#include "dr_wav_decoding.c"

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