├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── android ├── app │ ├── build.gradle │ ├── jni │ │ ├── Android.mk │ │ ├── Application.mk │ │ ├── SDL │ │ └── src │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── org │ │ │ ├── libsdl │ │ │ └── app │ │ │ │ ├── HIDDevice.java │ │ │ │ ├── HIDDeviceBLESteamController.java │ │ │ │ ├── HIDDeviceManager.java │ │ │ │ ├── HIDDeviceUSB.java │ │ │ │ ├── SDL.java │ │ │ │ ├── SDLActivity.java │ │ │ │ ├── SDLAudioManager.java │ │ │ │ └── SDLControllerManager.java │ │ │ └── tildearrow │ │ │ └── soundtracker │ │ │ └── soundtrackerActivity.java │ │ └── res │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── igfd ├── ImGuiFileDialog.cpp ├── ImGuiFileDialog.h ├── ImGuiFileDialogConfig.h ├── LICENSE ├── README.md ├── dirent │ ├── ChangeLog │ ├── LICENSE │ ├── README.md │ └── dirent.h └── stb │ ├── LICENSE │ ├── README.md │ ├── stb_image.h │ └── stb_image_resize.h ├── papers ├── format.md ├── help.txt ├── ibm-plex-license.txt ├── info.txt ├── ohplease.png ├── ssformat.md ├── ssiasm.md ├── tooltips.txt └── unifont-license.txt ├── soundtracker.kdev4 ├── src ├── Android.mk ├── blip_buf.c ├── blip_buf.h ├── blip_buf.txt ├── fextra.cpp ├── fextra.h ├── font_main.cpp ├── font_pat.cpp ├── fonts.h ├── hlesoundchip.cpp ├── macroStatus.cpp ├── main.cpp ├── nsstub.h ├── nsstub.m ├── player.cpp ├── song.cpp ├── soundchip.cpp ├── soundchip.h ├── ssbench.cpp ├── ssinter.cpp ├── ssinter.h ├── sslisp.cpp ├── ssmain.cpp ├── tests │ ├── duty │ ├── fdistort │ ├── filter │ ├── fsweep │ ├── fuzzie │ ├── psweep │ ├── shape │ └── vsweep ├── tracker.h ├── unifont.h ├── unifontfull.cpp ├── unifontfull.h ├── utfutils.cpp ├── utfutils.h └── winMain.cpp └── tools ├── CMakeLists.txt └── hex2bin.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .cache/ 2 | .kdev4/ 3 | .vscode/ 4 | build/ 5 | winbuild/ 6 | .DS_Store 7 | *.swp 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "SDL"] 2 | path = SDL 3 | url = https://github.com/libsdl-org/SDL 4 | branch = main 5 | [submodule "imgui"] 6 | path = imgui 7 | url = https://github.com/ocornut/imgui.git 8 | [submodule "fmt"] 9 | path = fmt 10 | url = https://github.com/fmtlib/fmt.git 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(soundtracker) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | if (ANDROID) 7 | set(BUILD_GUI OFF) 8 | else (ANDROID) 9 | set(BUILD_GUI ON) 10 | endif (ANDROID) 11 | 12 | if (WIN32) 13 | set(SDL2_LIB SDL2-static) 14 | else (WIN32) 15 | set(SDL2_LIB SDL2) 16 | endif (WIN32) 17 | 18 | add_subdirectory(fmt) 19 | 20 | include_directories(fmt) 21 | 22 | if (BUILD_GUI) 23 | if (WIN32) 24 | set(BUILD_SHARED_LIBS OFF) 25 | set(SDL_SHARED OFF) 26 | set(SDL_STATIC ON) 27 | endif (WIN32) 28 | add_subdirectory(SDL) 29 | include_directories(SDL/include) 30 | endif (BUILD_GUI) 31 | if (APPLE) 32 | find_library(HAVE_APPKIT AppKit PATHS ${CMAKE_OSX_SYSROOT}/System/Library PATH_SUFFIXES Frameworks NO_DEFAULT_PATH) 33 | else (APPLE) 34 | find_library(HAVE_JACK jack) 35 | endif (APPLE) 36 | add_executable(ssinter src/soundchip.cpp src/ssinter.cpp src/ssmain.cpp src/utfutils.cpp) 37 | add_executable(ssbench src/soundchip.cpp src/ssinter.cpp src/ssbench.cpp) 38 | set(GUI_SRC imgui/imgui.cpp 39 | imgui/imgui_demo.cpp 40 | imgui/imgui_draw.cpp 41 | imgui/imgui_tables.cpp 42 | imgui/imgui_widgets.cpp 43 | imgui/backends/imgui_impl_sdlrenderer.cpp 44 | imgui/backends/imgui_impl_sdl.cpp 45 | igfd/ImGuiFileDialog.cpp 46 | ) 47 | set(TRACKER_SRC src/blip_buf.c 48 | src/soundchip.cpp 49 | src/song.cpp 50 | src/player.cpp 51 | src/macroStatus.cpp 52 | src/hlesoundchip.cpp 53 | src/ssinter.cpp 54 | src/fextra.cpp 55 | src/utfutils.cpp 56 | src/font_main.cpp 57 | src/font_pat.cpp 58 | src/main.cpp) 59 | if (BUILD_GUI) 60 | list(APPEND TRACKER_SRC ${GUI_SRC}) 61 | endif (BUILD_GUI) 62 | if (APPLE) 63 | list(APPEND TRACKER_SRC src/nsstub.m) 64 | endif (APPLE) 65 | if (WIN32) 66 | add_executable(soundtracker WIN32 ${TRACKER_SRC}) 67 | else() 68 | add_executable(soundtracker ${TRACKER_SRC}) 69 | endif() 70 | target_link_libraries(ssinter ${SDL2_LIB}) 71 | if (BUILD_GUI) 72 | target_include_directories(soundtracker PRIVATE imgui imgui/backends igfd) 73 | target_compile_definitions(soundtracker PUBLIC HAVE_GUI) 74 | target_compile_definitions(ssinter PUBLIC HAVE_GUI) 75 | endif (BUILD_GUI) 76 | target_link_libraries(soundtracker ${SDL2_LIB} fmt) 77 | if (WIN32) 78 | target_link_libraries(ssinter SDL2main) 79 | target_link_libraries(soundtracker SDL2main -static) 80 | endif (WIN32) 81 | if (HAVE_JACK) 82 | target_compile_definitions(soundtracker PUBLIC JACK) 83 | target_link_libraries(soundtracker jack) 84 | target_compile_definitions(ssinter PUBLIC JACK) 85 | target_link_libraries(ssinter jack) 86 | endif () 87 | if (APPLE) 88 | target_link_libraries(soundtracker ${HAVE_APPKIT}) 89 | else () 90 | if (NOT WIN32) 91 | target_link_libraries(soundtracker X11) 92 | endif (NOT WIN32) 93 | endif () 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # soundtracker 2 | 3 | this is an old chiptune composition tool that I made as my first program back in 2014 (excluding the tutorials). 4 | 5 | this tool emulates/implements a fictitious soundchip that I thought of years ago, with heavy inspiration from the 8-bit era soundchips (like the SID). 6 | 7 | # and what is this "ssinter" thing? 8 | 9 | ssinter is an interpreter for the soundchip dump format. check [ssformat.md](papers/ssformat.md) for more information. 10 | 11 | # notes 12 | 13 | this project uses blip-buf, which is under the same license. 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY'); 2 | def buildAsApplication = !buildAsLibrary 3 | if (buildAsApplication) { 4 | apply plugin: 'com.android.application' 5 | } 6 | else { 7 | apply plugin: 'com.android.library' 8 | } 9 | 10 | android { 11 | compileSdkVersion 26 12 | defaultConfig { 13 | if (buildAsApplication) { 14 | applicationId "org.tildearrow.soundtracker" 15 | } 16 | minSdkVersion 16 17 | targetSdkVersion 26 18 | versionCode 1 19 | versionName "1.0" 20 | externalNativeBuild { 21 | ndkBuild { 22 | arguments "APP_PLATFORM=android-16" 23 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 24 | } 25 | // cmake { 26 | // arguments "-DANDROID_APP_PLATFORM=android-16", "-DANDROID_STL=c++_static" 27 | // // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' 28 | // abiFilters 'arm64-v8a' 29 | // } 30 | } 31 | } 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) { 39 | sourceSets.main { 40 | jniLibs.srcDir 'libs' 41 | } 42 | externalNativeBuild { 43 | ndkBuild { 44 | path 'jni/Android.mk' 45 | } 46 | // cmake { 47 | // path 'jni/CMakeLists.txt' 48 | // } 49 | } 50 | 51 | } 52 | lintOptions { 53 | abortOnError false 54 | } 55 | 56 | if (buildAsLibrary) { 57 | libraryVariants.all { variant -> 58 | variant.outputs.each { output -> 59 | def outputFile = output.outputFile 60 | if (outputFile != null && outputFile.name.endsWith(".aar")) { 61 | def fileName = "org.tildearrow.soundtracker.aar"; 62 | output.outputFile = new File(outputFile.parent, fileName); 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | dependencies { 70 | implementation fileTree(include: ['*.jar'], dir: 'libs') 71 | } 72 | -------------------------------------------------------------------------------- /android/app/jni/Android.mk: -------------------------------------------------------------------------------- 1 | include $(call all-subdir-makefiles) 2 | -------------------------------------------------------------------------------- /android/app/jni/Application.mk: -------------------------------------------------------------------------------- 1 | 2 | # Uncomment this if you're using STL in your project 3 | # You can find more information here: 4 | # https://developer.android.com/ndk/guides/cpp-support 5 | # APP_STL := c++_shared 6 | 7 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 8 | 9 | # Min runtime API level 10 | APP_PLATFORM=android-16 11 | -------------------------------------------------------------------------------- /android/app/jni/SDL: -------------------------------------------------------------------------------- 1 | ../../../SDL -------------------------------------------------------------------------------- /android/app/jni/src: -------------------------------------------------------------------------------- 1 | ../../../src -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in [sdk]/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 23 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 59 | 64 | 65 | 68 | 69 | 75 | 76 | 77 | 78 | 79 | 80 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /android/app/src/main/java/org/libsdl/app/HIDDevice.java: -------------------------------------------------------------------------------- 1 | package org.libsdl.app; 2 | 3 | import android.hardware.usb.UsbDevice; 4 | 5 | interface HIDDevice 6 | { 7 | public int getId(); 8 | public int getVendorId(); 9 | public int getProductId(); 10 | public String getSerialNumber(); 11 | public int getVersion(); 12 | public String getManufacturerName(); 13 | public String getProductName(); 14 | public UsbDevice getDevice(); 15 | public boolean open(); 16 | public int sendFeatureReport(byte[] report); 17 | public int sendOutputReport(byte[] report); 18 | public boolean getFeatureReport(byte[] report); 19 | public void setFrozen(boolean frozen); 20 | public void close(); 21 | public void shutdown(); 22 | } 23 | -------------------------------------------------------------------------------- /android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java: -------------------------------------------------------------------------------- 1 | package org.libsdl.app; 2 | 3 | import android.hardware.usb.*; 4 | import android.os.Build; 5 | import android.util.Log; 6 | import java.util.Arrays; 7 | 8 | class HIDDeviceUSB implements HIDDevice { 9 | 10 | private static final String TAG = "hidapi"; 11 | 12 | protected HIDDeviceManager mManager; 13 | protected UsbDevice mDevice; 14 | protected int mInterfaceIndex; 15 | protected int mInterface; 16 | protected int mDeviceId; 17 | protected UsbDeviceConnection mConnection; 18 | protected UsbEndpoint mInputEndpoint; 19 | protected UsbEndpoint mOutputEndpoint; 20 | protected InputThread mInputThread; 21 | protected boolean mRunning; 22 | protected boolean mFrozen; 23 | 24 | public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) { 25 | mManager = manager; 26 | mDevice = usbDevice; 27 | mInterfaceIndex = interface_index; 28 | mInterface = mDevice.getInterface(mInterfaceIndex).getId(); 29 | mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier()); 30 | mRunning = false; 31 | } 32 | 33 | public String getIdentifier() { 34 | return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex); 35 | } 36 | 37 | @Override 38 | public int getId() { 39 | return mDeviceId; 40 | } 41 | 42 | @Override 43 | public int getVendorId() { 44 | return mDevice.getVendorId(); 45 | } 46 | 47 | @Override 48 | public int getProductId() { 49 | return mDevice.getProductId(); 50 | } 51 | 52 | @Override 53 | public String getSerialNumber() { 54 | String result = null; 55 | if (Build.VERSION.SDK_INT >= 21) { 56 | result = mDevice.getSerialNumber(); 57 | } 58 | if (result == null) { 59 | result = ""; 60 | } 61 | return result; 62 | } 63 | 64 | @Override 65 | public int getVersion() { 66 | return 0; 67 | } 68 | 69 | @Override 70 | public String getManufacturerName() { 71 | String result = null; 72 | if (Build.VERSION.SDK_INT >= 21) { 73 | result = mDevice.getManufacturerName(); 74 | } 75 | if (result == null) { 76 | result = String.format("%x", getVendorId()); 77 | } 78 | return result; 79 | } 80 | 81 | @Override 82 | public String getProductName() { 83 | String result = null; 84 | if (Build.VERSION.SDK_INT >= 21) { 85 | result = mDevice.getProductName(); 86 | } 87 | if (result == null) { 88 | result = String.format("%x", getProductId()); 89 | } 90 | return result; 91 | } 92 | 93 | @Override 94 | public UsbDevice getDevice() { 95 | return mDevice; 96 | } 97 | 98 | public String getDeviceName() { 99 | return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")"; 100 | } 101 | 102 | @Override 103 | public boolean open() { 104 | mConnection = mManager.getUSBManager().openDevice(mDevice); 105 | if (mConnection == null) { 106 | Log.w(TAG, "Unable to open USB device " + getDeviceName()); 107 | return false; 108 | } 109 | 110 | // Force claim our interface 111 | UsbInterface iface = mDevice.getInterface(mInterfaceIndex); 112 | if (!mConnection.claimInterface(iface, true)) { 113 | Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName()); 114 | close(); 115 | return false; 116 | } 117 | 118 | // Find the endpoints 119 | for (int j = 0; j < iface.getEndpointCount(); j++) { 120 | UsbEndpoint endpt = iface.getEndpoint(j); 121 | switch (endpt.getDirection()) { 122 | case UsbConstants.USB_DIR_IN: 123 | if (mInputEndpoint == null) { 124 | mInputEndpoint = endpt; 125 | } 126 | break; 127 | case UsbConstants.USB_DIR_OUT: 128 | if (mOutputEndpoint == null) { 129 | mOutputEndpoint = endpt; 130 | } 131 | break; 132 | } 133 | } 134 | 135 | // Make sure the required endpoints were present 136 | if (mInputEndpoint == null || mOutputEndpoint == null) { 137 | Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName()); 138 | close(); 139 | return false; 140 | } 141 | 142 | // Start listening for input 143 | mRunning = true; 144 | mInputThread = new InputThread(); 145 | mInputThread.start(); 146 | 147 | return true; 148 | } 149 | 150 | @Override 151 | public int sendFeatureReport(byte[] report) { 152 | int res = -1; 153 | int offset = 0; 154 | int length = report.length; 155 | boolean skipped_report_id = false; 156 | byte report_number = report[0]; 157 | 158 | if (report_number == 0x0) { 159 | ++offset; 160 | --length; 161 | skipped_report_id = true; 162 | } 163 | 164 | res = mConnection.controlTransfer( 165 | UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT, 166 | 0x09/*HID set_report*/, 167 | (3/*HID feature*/ << 8) | report_number, 168 | mInterface, 169 | report, offset, length, 170 | 1000/*timeout millis*/); 171 | 172 | if (res < 0) { 173 | Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName()); 174 | return -1; 175 | } 176 | 177 | if (skipped_report_id) { 178 | ++length; 179 | } 180 | return length; 181 | } 182 | 183 | @Override 184 | public int sendOutputReport(byte[] report) { 185 | int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000); 186 | if (r != report.length) { 187 | Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName()); 188 | } 189 | return r; 190 | } 191 | 192 | @Override 193 | public boolean getFeatureReport(byte[] report) { 194 | int res = -1; 195 | int offset = 0; 196 | int length = report.length; 197 | boolean skipped_report_id = false; 198 | byte report_number = report[0]; 199 | 200 | if (report_number == 0x0) { 201 | /* Offset the return buffer by 1, so that the report ID 202 | will remain in byte 0. */ 203 | ++offset; 204 | --length; 205 | skipped_report_id = true; 206 | } 207 | 208 | res = mConnection.controlTransfer( 209 | UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN, 210 | 0x01/*HID get_report*/, 211 | (3/*HID feature*/ << 8) | report_number, 212 | mInterface, 213 | report, offset, length, 214 | 1000/*timeout millis*/); 215 | 216 | if (res < 0) { 217 | Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName()); 218 | return false; 219 | } 220 | 221 | if (skipped_report_id) { 222 | ++res; 223 | ++length; 224 | } 225 | 226 | byte[] data; 227 | if (res == length) { 228 | data = report; 229 | } else { 230 | data = Arrays.copyOfRange(report, 0, res); 231 | } 232 | mManager.HIDDeviceFeatureReport(mDeviceId, data); 233 | 234 | return true; 235 | } 236 | 237 | @Override 238 | public void close() { 239 | mRunning = false; 240 | if (mInputThread != null) { 241 | while (mInputThread.isAlive()) { 242 | mInputThread.interrupt(); 243 | try { 244 | mInputThread.join(); 245 | } catch (InterruptedException e) { 246 | // Keep trying until we're done 247 | } 248 | } 249 | mInputThread = null; 250 | } 251 | if (mConnection != null) { 252 | UsbInterface iface = mDevice.getInterface(mInterfaceIndex); 253 | mConnection.releaseInterface(iface); 254 | mConnection.close(); 255 | mConnection = null; 256 | } 257 | } 258 | 259 | @Override 260 | public void shutdown() { 261 | close(); 262 | mManager = null; 263 | } 264 | 265 | @Override 266 | public void setFrozen(boolean frozen) { 267 | mFrozen = frozen; 268 | } 269 | 270 | protected class InputThread extends Thread { 271 | @Override 272 | public void run() { 273 | int packetSize = mInputEndpoint.getMaxPacketSize(); 274 | byte[] packet = new byte[packetSize]; 275 | while (mRunning) { 276 | int r; 277 | try 278 | { 279 | r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000); 280 | } 281 | catch (Exception e) 282 | { 283 | Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e); 284 | break; 285 | } 286 | if (r < 0) { 287 | // Could be a timeout or an I/O error 288 | } 289 | if (r > 0) { 290 | byte[] data; 291 | if (r == packetSize) { 292 | data = packet; 293 | } else { 294 | data = Arrays.copyOfRange(packet, 0, r); 295 | } 296 | 297 | if (!mFrozen) { 298 | mManager.HIDDeviceInputReport(mDeviceId, data); 299 | } 300 | } 301 | } 302 | } 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /android/app/src/main/java/org/libsdl/app/SDL.java: -------------------------------------------------------------------------------- 1 | package org.libsdl.app; 2 | 3 | import android.content.Context; 4 | 5 | import java.lang.reflect.*; 6 | 7 | /** 8 | SDL library initialization 9 | */ 10 | public class SDL { 11 | 12 | // This function should be called first and sets up the native code 13 | // so it can call into the Java classes 14 | public static void setupJNI() { 15 | SDLActivity.nativeSetupJNI(); 16 | SDLAudioManager.nativeSetupJNI(); 17 | SDLControllerManager.nativeSetupJNI(); 18 | } 19 | 20 | // This function should be called each time the activity is started 21 | public static void initialize() { 22 | setContext(null); 23 | 24 | SDLActivity.initialize(); 25 | SDLAudioManager.initialize(); 26 | SDLControllerManager.initialize(); 27 | } 28 | 29 | // This function stores the current activity (SDL or not) 30 | public static void setContext(Context context) { 31 | mContext = context; 32 | } 33 | 34 | public static Context getContext() { 35 | return mContext; 36 | } 37 | 38 | public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException { 39 | 40 | if (libraryName == null) { 41 | throw new NullPointerException("No library name provided."); 42 | } 43 | 44 | try { 45 | // Let's see if we have ReLinker available in the project. This is necessary for 46 | // some projects that have huge numbers of local libraries bundled, and thus may 47 | // trip a bug in Android's native library loader which ReLinker works around. (If 48 | // loadLibrary works properly, ReLinker will simply use the normal Android method 49 | // internally.) 50 | // 51 | // To use ReLinker, just add it as a dependency. For more information, see 52 | // https://github.com/KeepSafe/ReLinker for ReLinker's repository. 53 | // 54 | Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker"); 55 | Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener"); 56 | Class contextClass = mContext.getClassLoader().loadClass("android.content.Context"); 57 | Class stringClass = mContext.getClassLoader().loadClass("java.lang.String"); 58 | 59 | // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if 60 | // they've changed during updates. 61 | Method forceMethod = relinkClass.getDeclaredMethod("force"); 62 | Object relinkInstance = forceMethod.invoke(null); 63 | Class relinkInstanceClass = relinkInstance.getClass(); 64 | 65 | // Actually load the library! 66 | Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass); 67 | loadMethod.invoke(relinkInstance, mContext, libraryName, null, null); 68 | } 69 | catch (final Throwable e) { 70 | // Fall back 71 | try { 72 | System.loadLibrary(libraryName); 73 | } 74 | catch (final UnsatisfiedLinkError ule) { 75 | throw ule; 76 | } 77 | catch (final SecurityException se) { 78 | throw se; 79 | } 80 | } 81 | } 82 | 83 | protected static Context mContext; 84 | } 85 | -------------------------------------------------------------------------------- /android/app/src/main/java/org/libsdl/app/SDLAudioManager.java: -------------------------------------------------------------------------------- 1 | package org.libsdl.app; 2 | 3 | import android.media.*; 4 | import android.os.Build; 5 | import android.util.Log; 6 | 7 | public class SDLAudioManager 8 | { 9 | protected static final String TAG = "SDLAudio"; 10 | 11 | protected static AudioTrack mAudioTrack; 12 | protected static AudioRecord mAudioRecord; 13 | 14 | public static void initialize() { 15 | mAudioTrack = null; 16 | mAudioRecord = null; 17 | } 18 | 19 | // Audio 20 | 21 | protected static String getAudioFormatString(int audioFormat) { 22 | switch (audioFormat) { 23 | case AudioFormat.ENCODING_PCM_8BIT: 24 | return "8-bit"; 25 | case AudioFormat.ENCODING_PCM_16BIT: 26 | return "16-bit"; 27 | case AudioFormat.ENCODING_PCM_FLOAT: 28 | return "float"; 29 | default: 30 | return Integer.toString(audioFormat); 31 | } 32 | } 33 | 34 | protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) { 35 | int channelConfig; 36 | int sampleSize; 37 | int frameSize; 38 | 39 | Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz"); 40 | 41 | /* On older devices let's use known good settings */ 42 | if (Build.VERSION.SDK_INT < 21) { 43 | if (desiredChannels > 2) { 44 | desiredChannels = 2; 45 | } 46 | if (sampleRate < 8000) { 47 | sampleRate = 8000; 48 | } else if (sampleRate > 48000) { 49 | sampleRate = 48000; 50 | } 51 | } 52 | 53 | if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) { 54 | int minSDKVersion = (isCapture ? 23 : 21); 55 | if (Build.VERSION.SDK_INT < minSDKVersion) { 56 | audioFormat = AudioFormat.ENCODING_PCM_16BIT; 57 | } 58 | } 59 | switch (audioFormat) 60 | { 61 | case AudioFormat.ENCODING_PCM_8BIT: 62 | sampleSize = 1; 63 | break; 64 | case AudioFormat.ENCODING_PCM_16BIT: 65 | sampleSize = 2; 66 | break; 67 | case AudioFormat.ENCODING_PCM_FLOAT: 68 | sampleSize = 4; 69 | break; 70 | default: 71 | Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT"); 72 | audioFormat = AudioFormat.ENCODING_PCM_16BIT; 73 | sampleSize = 2; 74 | break; 75 | } 76 | 77 | if (isCapture) { 78 | switch (desiredChannels) { 79 | case 1: 80 | channelConfig = AudioFormat.CHANNEL_IN_MONO; 81 | break; 82 | case 2: 83 | channelConfig = AudioFormat.CHANNEL_IN_STEREO; 84 | break; 85 | default: 86 | Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo"); 87 | desiredChannels = 2; 88 | channelConfig = AudioFormat.CHANNEL_IN_STEREO; 89 | break; 90 | } 91 | } else { 92 | switch (desiredChannels) { 93 | case 1: 94 | channelConfig = AudioFormat.CHANNEL_OUT_MONO; 95 | break; 96 | case 2: 97 | channelConfig = AudioFormat.CHANNEL_OUT_STEREO; 98 | break; 99 | case 3: 100 | channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER; 101 | break; 102 | case 4: 103 | channelConfig = AudioFormat.CHANNEL_OUT_QUAD; 104 | break; 105 | case 5: 106 | channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER; 107 | break; 108 | case 6: 109 | channelConfig = AudioFormat.CHANNEL_OUT_5POINT1; 110 | break; 111 | case 7: 112 | channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER; 113 | break; 114 | case 8: 115 | if (Build.VERSION.SDK_INT >= 23) { 116 | channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND; 117 | } else { 118 | Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround"); 119 | desiredChannels = 6; 120 | channelConfig = AudioFormat.CHANNEL_OUT_5POINT1; 121 | } 122 | break; 123 | default: 124 | Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo"); 125 | desiredChannels = 2; 126 | channelConfig = AudioFormat.CHANNEL_OUT_STEREO; 127 | break; 128 | } 129 | 130 | /* 131 | Log.v(TAG, "Speaker configuration (and order of channels):"); 132 | 133 | if ((channelConfig & 0x00000004) != 0) { 134 | Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT"); 135 | } 136 | if ((channelConfig & 0x00000008) != 0) { 137 | Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT"); 138 | } 139 | if ((channelConfig & 0x00000010) != 0) { 140 | Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER"); 141 | } 142 | if ((channelConfig & 0x00000020) != 0) { 143 | Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY"); 144 | } 145 | if ((channelConfig & 0x00000040) != 0) { 146 | Log.v(TAG, " CHANNEL_OUT_BACK_LEFT"); 147 | } 148 | if ((channelConfig & 0x00000080) != 0) { 149 | Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT"); 150 | } 151 | if ((channelConfig & 0x00000100) != 0) { 152 | Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER"); 153 | } 154 | if ((channelConfig & 0x00000200) != 0) { 155 | Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER"); 156 | } 157 | if ((channelConfig & 0x00000400) != 0) { 158 | Log.v(TAG, " CHANNEL_OUT_BACK_CENTER"); 159 | } 160 | if ((channelConfig & 0x00000800) != 0) { 161 | Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT"); 162 | } 163 | if ((channelConfig & 0x00001000) != 0) { 164 | Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT"); 165 | } 166 | */ 167 | } 168 | frameSize = (sampleSize * desiredChannels); 169 | 170 | // Let the user pick a larger buffer if they really want -- but ye 171 | // gods they probably shouldn't, the minimums are horrifyingly high 172 | // latency already 173 | int minBufferSize; 174 | if (isCapture) { 175 | minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); 176 | } else { 177 | minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat); 178 | } 179 | desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize); 180 | 181 | int[] results = new int[4]; 182 | 183 | if (isCapture) { 184 | if (mAudioRecord == null) { 185 | mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, 186 | channelConfig, audioFormat, desiredFrames * frameSize); 187 | 188 | // see notes about AudioTrack state in audioOpen(), above. Probably also applies here. 189 | if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) { 190 | Log.e(TAG, "Failed during initialization of AudioRecord"); 191 | mAudioRecord.release(); 192 | mAudioRecord = null; 193 | return null; 194 | } 195 | 196 | mAudioRecord.startRecording(); 197 | } 198 | 199 | results[0] = mAudioRecord.getSampleRate(); 200 | results[1] = mAudioRecord.getAudioFormat(); 201 | results[2] = mAudioRecord.getChannelCount(); 202 | results[3] = desiredFrames; 203 | 204 | } else { 205 | if (mAudioTrack == null) { 206 | mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); 207 | 208 | // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid 209 | // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java 210 | // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState() 211 | if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) { 212 | /* Try again, with safer values */ 213 | 214 | Log.e(TAG, "Failed during initialization of Audio Track"); 215 | mAudioTrack.release(); 216 | mAudioTrack = null; 217 | return null; 218 | } 219 | 220 | mAudioTrack.play(); 221 | } 222 | 223 | results[0] = mAudioTrack.getSampleRate(); 224 | results[1] = mAudioTrack.getAudioFormat(); 225 | results[2] = mAudioTrack.getChannelCount(); 226 | results[3] = desiredFrames; 227 | } 228 | 229 | Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz"); 230 | 231 | return results; 232 | } 233 | 234 | /** 235 | * This method is called by SDL using JNI. 236 | */ 237 | public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) { 238 | return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames); 239 | } 240 | 241 | /** 242 | * This method is called by SDL using JNI. 243 | */ 244 | public static void audioWriteFloatBuffer(float[] buffer) { 245 | if (mAudioTrack == null) { 246 | Log.e(TAG, "Attempted to make audio call with uninitialized audio!"); 247 | return; 248 | } 249 | 250 | for (int i = 0; i < buffer.length;) { 251 | int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING); 252 | if (result > 0) { 253 | i += result; 254 | } else if (result == 0) { 255 | try { 256 | Thread.sleep(1); 257 | } catch(InterruptedException e) { 258 | // Nom nom 259 | } 260 | } else { 261 | Log.w(TAG, "SDL audio: error return from write(float)"); 262 | return; 263 | } 264 | } 265 | } 266 | 267 | /** 268 | * This method is called by SDL using JNI. 269 | */ 270 | public static void audioWriteShortBuffer(short[] buffer) { 271 | if (mAudioTrack == null) { 272 | Log.e(TAG, "Attempted to make audio call with uninitialized audio!"); 273 | return; 274 | } 275 | 276 | for (int i = 0; i < buffer.length;) { 277 | int result = mAudioTrack.write(buffer, i, buffer.length - i); 278 | if (result > 0) { 279 | i += result; 280 | } else if (result == 0) { 281 | try { 282 | Thread.sleep(1); 283 | } catch(InterruptedException e) { 284 | // Nom nom 285 | } 286 | } else { 287 | Log.w(TAG, "SDL audio: error return from write(short)"); 288 | return; 289 | } 290 | } 291 | } 292 | 293 | /** 294 | * This method is called by SDL using JNI. 295 | */ 296 | public static void audioWriteByteBuffer(byte[] buffer) { 297 | if (mAudioTrack == null) { 298 | Log.e(TAG, "Attempted to make audio call with uninitialized audio!"); 299 | return; 300 | } 301 | 302 | for (int i = 0; i < buffer.length; ) { 303 | int result = mAudioTrack.write(buffer, i, buffer.length - i); 304 | if (result > 0) { 305 | i += result; 306 | } else if (result == 0) { 307 | try { 308 | Thread.sleep(1); 309 | } catch(InterruptedException e) { 310 | // Nom nom 311 | } 312 | } else { 313 | Log.w(TAG, "SDL audio: error return from write(byte)"); 314 | return; 315 | } 316 | } 317 | } 318 | 319 | /** 320 | * This method is called by SDL using JNI. 321 | */ 322 | public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) { 323 | return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames); 324 | } 325 | 326 | /** This method is called by SDL using JNI. */ 327 | public static int captureReadFloatBuffer(float[] buffer, boolean blocking) { 328 | return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); 329 | } 330 | 331 | /** This method is called by SDL using JNI. */ 332 | public static int captureReadShortBuffer(short[] buffer, boolean blocking) { 333 | if (Build.VERSION.SDK_INT < 23) { 334 | return mAudioRecord.read(buffer, 0, buffer.length); 335 | } else { 336 | return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); 337 | } 338 | } 339 | 340 | /** This method is called by SDL using JNI. */ 341 | public static int captureReadByteBuffer(byte[] buffer, boolean blocking) { 342 | if (Build.VERSION.SDK_INT < 23) { 343 | return mAudioRecord.read(buffer, 0, buffer.length); 344 | } else { 345 | return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); 346 | } 347 | } 348 | 349 | /** This method is called by SDL using JNI. */ 350 | public static void audioClose() { 351 | if (mAudioTrack != null) { 352 | mAudioTrack.stop(); 353 | mAudioTrack.release(); 354 | mAudioTrack = null; 355 | } 356 | } 357 | 358 | /** This method is called by SDL using JNI. */ 359 | public static void captureClose() { 360 | if (mAudioRecord != null) { 361 | mAudioRecord.stop(); 362 | mAudioRecord.release(); 363 | mAudioRecord = null; 364 | } 365 | } 366 | 367 | /** This method is called by SDL using JNI. */ 368 | public static void audioSetThreadPriority(boolean iscapture, int device_id) { 369 | try { 370 | 371 | /* Set thread name */ 372 | if (iscapture) { 373 | Thread.currentThread().setName("SDLAudioC" + device_id); 374 | } else { 375 | Thread.currentThread().setName("SDLAudioP" + device_id); 376 | } 377 | 378 | /* Set thread priority */ 379 | android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO); 380 | 381 | } catch (Exception e) { 382 | Log.v(TAG, "modify thread properties failed " + e.toString()); 383 | } 384 | } 385 | 386 | public static native int nativeSetupJNI(); 387 | } 388 | -------------------------------------------------------------------------------- /android/app/src/main/java/org/tildearrow/soundtracker/soundtrackerActivity.java: -------------------------------------------------------------------------------- 1 | package org.tildearrow.soundtracker; 2 | 3 | import org.libsdl.app.SDLActivity; 4 | 5 | public class soundtrackerActivity extends SDLActivity 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tildearrow/soundtracker/4240845b5e00510128240222ce66d87fbe71daf7/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tildearrow/soundtracker/4240845b5e00510128240222ce66d87fbe71daf7/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tildearrow/soundtracker/4240845b5e00510128240222ce66d87fbe71daf7/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tildearrow/soundtracker/4240845b5e00510128240222ce66d87fbe71daf7/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tildearrow/soundtracker/4240845b5e00510128240222ce66d87fbe71daf7/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | soundtracker 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.2.0' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | google() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tildearrow/soundtracker/4240845b5e00510128240222ce66d87fbe71daf7/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Oct 23 13:51:26 PDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /igfd/ImGuiFileDialogConfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // uncomment and modify defines under for customize ImGuiFileDialog 4 | 5 | //this options need c++17 6 | //#define USE_STD_FILESYSTEM 7 | 8 | //#define MAX_FILE_DIALOG_NAME_BUFFER 1024 9 | //#define MAX_PATH_BUFFER_SIZE 1024 10 | 11 | //#define USE_THUMBNAILS 12 | //the thumbnail generation use the stb_image and stb_resize lib who need to define the implementation 13 | //btw if you already use them in your app, you can have compiler error due to "implemntation found in double" 14 | //so uncomment these line for prevent the creation of implementation of these libs again 15 | //#define DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION 16 | //#define DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION 17 | //#define IMGUI_RADIO_BUTTON RadioButton 18 | //#define DisplayMode_ThumbailsList_ImageHeight 32.0f 19 | //#define tableHeaderFileThumbnailsString "Thumbnails" 20 | //#define DisplayMode_FilesList_ButtonString "FL" 21 | //#define DisplayMode_FilesList_ButtonHelp "File List" 22 | //#define DisplayMode_ThumbailsList_ButtonString "TL" 23 | //#define DisplayMode_ThumbailsList_ButtonHelp "Thumbnails List" 24 | // todo 25 | //#define DisplayMode_ThumbailsGrid_ButtonString "TG" 26 | //#define DisplayMode_ThumbailsGrid_ButtonHelp "Thumbnails Grid" 27 | 28 | 29 | //#define USE_EXPLORATION_BY_KEYS 30 | // this mapping by default is for GLFW but you can use another 31 | //#include 32 | // Up key for explore to the top 33 | //#define IGFD_KEY_UP GLFW_KEY_UP 34 | // Down key for explore to the bottom 35 | //#define IGFD_KEY_DOWN GLFW_KEY_DOWN 36 | // Enter key for open directory 37 | //#define IGFD_KEY_ENTER GLFW_KEY_ENTER 38 | // BackSpace for comming back to the last directory 39 | //#define IGFD_KEY_BACKSPACE GLFW_KEY_BACKSPACE 40 | 41 | // by ex you can quit the dialog by pressing the key excape 42 | //#define USE_DIALOG_EXIT_WITH_KEY 43 | //#define IGFD_EXIT_KEY GLFW_KEY_ESCAPE 44 | 45 | // widget 46 | // filter combobox width 47 | //#define FILTER_COMBO_WIDTH 120.0f 48 | // button widget use for compose path 49 | //#define IMGUI_PATH_BUTTON ImGui::Button 50 | // standard button 51 | //#define IMGUI_BUTTON ImGui::Button 52 | 53 | // locales string 54 | //#define createDirButtonString "+" 55 | //#define okButtonString " OK" 56 | //#define cancelButtonString " Cancel" 57 | //#define resetButtonString "R" 58 | //#define drivesButtonString "Drives" 59 | //#define editPathButtonString "E" 60 | //#define searchString "Search" 61 | //#define dirEntryString "[DIR] " 62 | //#define linkEntryString "[LINK] " 63 | //#define fileEntryString "[FILE] " 64 | //#define fileNameString "File Name : " 65 | //#define dirNameString "Directory Path :" 66 | //#define buttonResetSearchString "Reset search" 67 | //#define buttonDriveString "Drives" 68 | //#define buttonEditPathString "Edit path\nYou can also right click on path buttons" 69 | //#define buttonResetPathString "Reset to current directory" 70 | //#define buttonCreateDirString "Create Directory" 71 | //#define OverWriteDialogTitleString "The file Already Exist !" 72 | //#define OverWriteDialogMessageString "Would you like to OverWrite it ?" 73 | //#define OverWriteDialogConfirmButtonString "Confirm" 74 | //#define OverWriteDialogCancelButtonString "Cancel" 75 | 76 | // DateTimeFormat 77 | // see strftime functionin for customize 78 | // "%Y/%m/%d %H:%M" give 2021:01:22 11:47 79 | // "%Y/%m/%d %i:%M%p" give 2021:01:22 11:45PM 80 | //#define DateTimeFormat "%Y/%m/%d %i:%M%p" 81 | 82 | // theses icons will appear in table headers 83 | //#define USE_CUSTOM_SORTING_ICON 84 | //#define tableHeaderAscendingIcon "A|" 85 | //#define tableHeaderDescendingIcon "D|" 86 | //#define tableHeaderFileNameString " File name" 87 | //#define tableHeaderFileTypeString " Type" 88 | //#define tableHeaderFileSizeString " Size" 89 | //#define tableHeaderFileDateTimeString " Date" 90 | 91 | //#define USE_BOOKMARK 92 | //#define bookmarkPaneWith 150.0f 93 | //#define IMGUI_TOGGLE_BUTTON ToggleButton 94 | //#define bookmarksButtonString "Bookmark" 95 | //#define bookmarksButtonHelpString "Bookmark" 96 | //#define addBookmarkButtonString "+" 97 | //#define removeBookmarkButtonString "-" 98 | -------------------------------------------------------------------------------- /igfd/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2020 Stephane Cuillerdier (aka Aiekick) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /igfd/dirent/ChangeLog: -------------------------------------------------------------------------------- 1 | 2018-05-08 Toni Rönkkö 2 | 3 | * Version 1.23.2: fixes bad scandir prototype. 4 | 5 | 2017-08-27 Toni Rönkkö 6 | 7 | * Version 1.23: support readdir_r and scandir functions. 8 | 9 | 2017-07-18 Toni Rönkkö 10 | 11 | * Created release branches v1.22 and v1.21 to Git. Published version 12 | 1.22 at softagalleria.net. 13 | 14 | 2016-09-11 Toni Rönkkö 15 | 16 | * Version 1.22: added support for CMake. Thanks to Paul Fultz II. 17 | 18 | 2014-09-25 Toni Rönkkö 19 | 20 | * Version 1.21: compiles correctly under Open Watcom. Thanks to 21 | Virgil Banowetz for a patch! 22 | 23 | 2014-04-07 Toni Rönkkö 24 | 25 | * Version 1.20.1: the zip file from the previous version did not open 26 | correctly with Microsoft's compressed folders. Thanks to Alexandre 27 | for info! 28 | 29 | 2014-03-17 Toni Ronkko 30 | 31 | * Version 1.20: dirent.h compiles correctly in 64-bit architecture. 32 | Thanks to Aaron Simmons! 33 | 34 | 2014-03-03 Toni Ronkko 35 | 36 | * Version 1.13.2: define DT_LNK for compatibility with Unix 37 | programs. Thanks to Joel Bruick for suggestion! 38 | 39 | 2013-01-27 Toni Ronkko 40 | 41 | * Version 1.13.1: patch from Edward Berner fixes set_errno() on 42 | Windows NT 4.0. 43 | 44 | * Revised wcstombs() and mbstowcs() wrappers to make sure that they do 45 | not write past their target string. 46 | 47 | * PATH_MAX from windows.h includes zero terminator so there is no 48 | need to add one extra byte to variables and structures. 49 | 50 | 2012-12-12 Toni Ronkko 51 | 52 | * Version 1.13: use the traditional 8+3 file naming scheme if a file 53 | name cannot be represented in the default ANSI code page. Now 54 | compiles again with MSVC 6.0. Thanks to Konstantin Khomoutov for 55 | testing. 56 | 57 | 2012-10-01 Toni Ronkko 58 | 59 | * Version 1.12.1: renamed wide-character DIR structure _wDIR to 60 | _WDIR (with capital W) in order to maintain compatibility with MingW. 61 | 62 | 2012-09-30 Toni Ronkko 63 | 64 | * Version 1.12: define PATH_MAX and NAME_MAX. Added wide-character 65 | variants _wDIR, _wdirent, _wopendir(), _wreaddir(), _wclosedir() and 66 | _wrewinddir(). Thanks to Edgar Buerkle and Jan Nijtmans for ideas 67 | and code. 68 | 69 | * Now avoiding windows.h. This allows dirent.h to be integrated 70 | more easily into programs using winsock. Thanks to Fernando 71 | Azaldegui. 72 | 73 | 2011-03-15 Toni Ronkko 74 | 75 | * Version 1.11: defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0. 76 | 77 | 2010-08-11 Toni Ronkko 78 | 79 | * Version 1.10: added d_type and d_namlen fields to dirent structure. 80 | The former is especially useful for determining whether directory 81 | entry represents a file or a directory. For more information, see 82 | http://www.delorie.com/gnu/docs/glibc/libc_270.html 83 | 84 | * Improved conformance to the standards. For example, errno is now 85 | set properly on failure and assert() is never used. Thanks to Peter 86 | Brockam for suggestions. 87 | 88 | * Fixed a bug in rewinddir(): when using relative directory names, 89 | change of working directory no longer causes rewinddir() to fail. 90 | 91 | 2009-12-15 John Cunningham 92 | 93 | * Version 1.9: added rewinddir member function 94 | 95 | 2008-01-18 Toni Ronkko 96 | 97 | * Version 1.8: Using FindFirstFileA and WIN32_FIND_DATAA to avoid 98 | converting string between multi-byte and unicode representations. 99 | This makes the code simpler and also allows the code to be compiled 100 | under MingW. Thanks to Azriel Fasten for the suggestion. 101 | 102 | 2007-03-04 Toni Ronkko 103 | 104 | * Bug fix: due to the strncpy_s() function this file only compiled in 105 | Visual Studio 2005. Using the new string functions only when the 106 | compiler version allows. 107 | 108 | 2006-11-02 Toni Ronkko 109 | 110 | * Major update: removed support for Watcom C, MS-DOS and Turbo C to 111 | simplify the file, updated the code to compile cleanly on Visual 112 | Studio 2005 with both unicode and multi-byte character strings, 113 | removed rewinddir() as it had a bug. 114 | 115 | 2006-08-20 Toni Ronkko 116 | 117 | * Removed all remarks about MSVC 1.0, which is antiqued now. 118 | Simplified comments by removing SGML tags. 119 | 120 | 2002-05-14 Toni Ronkko 121 | 122 | * Embedded the function definitions directly to the header so that no 123 | source modules need to be included in the Visual Studio project. 124 | Removed all the dependencies to other projects so that this header 125 | file can be used independently. 126 | 127 | 1998-05-28 Toni Ronkko 128 | 129 | * First version. 130 | -------------------------------------------------------------------------------- /igfd/dirent/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 1998-2019 Toni Ronkko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /igfd/dirent/README.md: -------------------------------------------------------------------------------- 1 | # Dirent 2 | Dirent is a C/C++ programming interface that allows programmers to retrieve 3 | information about files and directories under Linux/UNIX. This project 4 | provides Linux compatible Dirent interface for Microsoft Windows. 5 | 6 | 7 | # Installation 8 | 9 | Download the latest Dirent installation package from 10 | [GitHub](https://github.com/tronkko/dirent/releases) and 11 | unpack the installation file with 7-zip, for example. The installation 12 | package contains dirent.h file as well as a few example programs and 13 | tests. 14 | 15 | 16 | ## Install Dirent for All Programs 17 | 18 | To make dirent.h available for all C/C++ programs, simply copy the 19 | ``include/dirent.h`` file to the system include directory. System include 20 | directory contains header files such as assert.h and windows.h. In Visual 21 | Studio 2008, for example, the system include may be found at 22 | ``C:\Program Files\Microsoft Visual Studio 9.0\VC\include``. 23 | 24 | Everything you need is included in the single dirent.h file, and you can 25 | start using Dirent immediately -- there is no need to add files to your 26 | Visual Studio project. 27 | 28 | 29 | ## Embed Dirent into Your Own Project 30 | 31 | If you wish to distribute dirent.h alongside with your own source code, then 32 | copy ``include/dirent.h`` file to a new sub-directory within your project and 33 | add that directory to include path on Windows while omitting the directory 34 | under Linux/UNIX. This allows your project to be compiled against native 35 | dirent.h on Linux/UNIX while substituting the functionality on Microsoft 36 | Windows. 37 | 38 | 39 | ## Examples 40 | 41 | The installation package contains four example programs: 42 | 43 | Program | Purpose 44 | -------- | ----------------------------------------------------------------- 45 | ls | List files in a directory, e.g. ls "c:\Program Files" 46 | find | Find files in subdirectories, e.g. find "c:\Program Files\CMake" 47 | updatedb | Build database of files in a drive, e.g. updatedb c:\ 48 | locate | Locate a file from database, e.g. locate notepad.exe 49 | 50 | To build the example programs, first install [CMake](https://cmake.org/). 51 | Then, with CMake installed, open command prompt and create a temporary 52 | directory ``c:\temp\dirent`` for the build files as 53 | 54 | ``` 55 | c:\ 56 | mkdir temp 57 | mkdir temp\dirent 58 | cd temp\dirent 59 | ``` 60 | 61 | Generate build files as 62 | 63 | ``` 64 | cmake d:\dirent 65 | ``` 66 | 67 | where ``d:\dirent`` is the root directory of the Dirent package (containing 68 | this README.md and LICENSE file). 69 | 70 | Once CMake is finished, open Visual Studio, load the generated dirent.sln file 71 | from the build directory and build the solution. Once the build completes, run 72 | the example programs from the command prompt as 73 | 74 | ``` 75 | cd Debug 76 | ls . 77 | find . 78 | updatedb c:\ 79 | locate cmd.exe 80 | ``` 81 | 82 | 83 | # Copying 84 | 85 | Dirent may be freely distributed under the MIT license. See the 86 | [LICENSE](LICENSE) file for details. 87 | 88 | 89 | # Alternatives to Dirent 90 | 91 | I ported Dirent to Microsoft Windows in 1998 when only a few alternatives 92 | were available. However, the situation has changed since then and nowadays 93 | both [Cygwin](http://www.cygwin.com) and [MingW](http://www.mingw.org) 94 | allow you to compile a great number of UNIX programs in Microsoft Windows. 95 | They both provide a full dirent API as well as many other UNIX APIs. MingW 96 | can even be used for commercial applications! 97 | -------------------------------------------------------------------------------- /igfd/stb/LICENSE: -------------------------------------------------------------------------------- 1 | This software is available under 2 licenses -- choose whichever you prefer. 2 | ------------------------------------------------------------------------------ 3 | ALTERNATIVE A - MIT License 4 | Copyright (c) 2017 Sean Barrett 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | ------------------------------------------------------------------------------ 21 | ALTERNATIVE B - Public Domain (www.unlicense.org) 22 | This is free and unencumbered software released into the public domain. 23 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 24 | software, either in source code form or as a compiled binary, for any purpose, 25 | commercial or non-commercial, and by any means. 26 | In jurisdictions that recognize copyright laws, the author or authors of this 27 | software dedicate any and all copyright interest in the software to the public 28 | domain. We make this dedication for the benefit of the public at large and to 29 | the detriment of our heirs and successors. We intend this dedication to be an 30 | overt act of relinquishment in perpetuity of all present and future rights to 31 | this software under copyright law. 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 36 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 37 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /igfd/stb/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | stb 4 | === 5 | 6 | single-file public domain (or MIT licensed) libraries for C/C++ 7 | 8 | Noteworthy: 9 | 10 | * image loader: [stb_image.h](stb_image.h) 11 | * image writer: [stb_image_write.h](stb_image_write.h) 12 | * image resizer: [stb_image_resize.h](stb_image_resize.h) 13 | * font text rasterizer: [stb_truetype.h](stb_truetype.h) 14 | * typesafe containers: [stb_ds.h](stb_ds.h) 15 | 16 | Most libraries by stb, except: stb_dxt by Fabian "ryg" Giesen, stb_image_resize 17 | by Jorge L. "VinoBS" Rodriguez, and stb_sprintf by Jeff Roberts. 18 | 19 | 20 | 21 | library | lastest version | category | LoC | description 22 | --------------------- | ---- | -------- | --- | -------------------------------- 23 | **[stb_vorbis.c](stb_vorbis.c)** | 1.20 | audio | 5563 | decode ogg vorbis files from file/memory to float/16-bit signed output 24 | **[stb_image.h](stb_image.h)** | 2.26 | graphics | 7762 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC 25 | **[stb_truetype.h](stb_truetype.h)** | 1.24 | graphics | 5011 | parse, decode, and rasterize characters from truetype fonts 26 | **[stb_image_write.h](stb_image_write.h)** | 1.15 | graphics | 1690 | image writing to disk: PNG, TGA, BMP 27 | **[stb_image_resize.h](stb_image_resize.h)** | 0.96 | graphics | 2631 | resize images larger/smaller with good quality 28 | **[stb_rect_pack.h](stb_rect_pack.h)** | 1.00 | graphics | 628 | simple 2D rectangle packer with decent quality 29 | **[stb_ds.h](stb_ds.h)** | 0.65 | utility | 1880 | typesafe dynamic array and hash tables for C, will compile in C++ 30 | **[stb_sprintf.h](stb_sprintf.h)** | 1.09 | utility | 1879 | fast sprintf, snprintf for C/C++ 31 | **[stretchy_buffer.h](stretchy_buffer.h)** | 1.04 | utility | 263 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ 32 | **[stb_textedit.h](stb_textedit.h)** | 1.13 | user interface | 1404 | guts of a text editor for games etc implementing them from scratch 33 | **[stb_voxel_render.h](stb_voxel_render.h)** | 0.89 | 3D graphics | 3807 | Minecraft-esque voxel rendering "engine" with many more features 34 | **[stb_dxt.h](stb_dxt.h)** | 1.10 | 3D graphics | 753 | Fabian "ryg" Giesen's real-time DXT compressor 35 | **[stb_perlin.h](stb_perlin.h)** | 0.5 | 3D graphics | 428 | revised Perlin noise (3D input, 1D output) 36 | **[stb_easy_font.h](stb_easy_font.h)** | 1.1 | 3D graphics | 305 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc 37 | **[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.41 | game dev | 4161 | embeddable tilemap editor 38 | **[stb_herringbone_wa...](stb_herringbone_wang_tile.h)** | 0.7 | game dev | 1221 | herringbone Wang tile map generator 39 | **[stb_c_lexer.h](stb_c_lexer.h)** | 0.11 | parsing | 966 | simplify writing parsers for C-like languages 40 | **[stb_divide.h](stb_divide.h)** | 0.93 | math | 430 | more useful 32-bit modulus e.g. "euclidean divide" 41 | **[stb_connected_comp...](stb_connected_components.h)** | 0.96 | misc | 1049 | incrementally compute reachability on grids 42 | **[stb.h](stb.h)** | 2.37 | misc | 14454 | helper functions for C, mostly redundant in C++; basically author's personal stuff 43 | **[stb_leakcheck.h](stb_leakcheck.h)** | 0.6 | misc | 194 | quick-and-dirty malloc/free leak-checking 44 | **[stb_include.h](stb_include.h)** | 0.02 | misc | 295 | implement recursive #include support, particularly for GLSL 45 | 46 | Total libraries: 22 47 | Total lines of C code: 56774 48 | 49 | 50 | FAQ 51 | --- 52 | 53 | #### What's the license? 54 | 55 | These libraries are in the public domain. You can do anything you 56 | want with them. You have no legal obligation 57 | to do anything else, although I appreciate attribution. 58 | 59 | They are also licensed under the MIT open source license, if you have lawyers 60 | who are unhappy with public domain. Every source file includes an explicit 61 | dual-license for you to choose from. 62 | 63 | #### Are there other single-file public-domain/open source libraries with minimal dependencies out there? 64 | 65 | [Yes.](https://github.com/nothings/single_file_libs) 66 | 67 | #### If I wrap an stb library in a new library, does the new library have to be public domain/MIT? 68 | 69 | No, because it's public domain you can freely relicense it to whatever license your new 70 | library wants to be. 71 | 72 | #### What's the deal with SSE support in GCC-based compilers? 73 | 74 | stb_image will either use SSE2 (if you compile with -msse2) or 75 | will not use any SIMD at all, rather than trying to detect the 76 | processor at runtime and handle it correctly. As I understand it, 77 | the approved path in GCC for runtime-detection require 78 | you to use multiple source files, one for each CPU configuration. 79 | Because stb_image is a header-file library that compiles in only 80 | one source file, there's no approved way to build both an 81 | SSE-enabled and a non-SSE-enabled variation. 82 | 83 | While we've tried to work around it, we've had multiple issues over 84 | the years due to specific versions of gcc breaking what we're doing, 85 | so we've given up on it. See https://github.com/nothings/stb/issues/280 86 | and https://github.com/nothings/stb/issues/410 for examples. 87 | 88 | #### Some of these libraries seem redundant to existing open source libraries. Are they better somehow? 89 | 90 | Generally they're only better in that they're easier to integrate, 91 | easier to use, and easier to release (single file; good API; no 92 | attribution requirement). They may be less featureful, slower, 93 | and/or use more memory. If you're already using an equivalent 94 | library, there's probably no good reason to switch. 95 | 96 | #### Can I link directly to the table of stb libraries? 97 | 98 | You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list. 99 | 100 | #### Why do you list "lines of code"? It's a terrible metric. 101 | 102 | Just to give you some idea of the internal complexity of the library, 103 | to help you manage your expectations, or to let you know what you're 104 | getting into. While not all the libraries are written in the same 105 | style, they're certainly similar styles, and so comparisons between 106 | the libraries are probably still meaningful. 107 | 108 | Note though that the lines do include both the implementation, the 109 | part that corresponds to a header file, and the documentation. 110 | 111 | #### Why single-file headers? 112 | 113 | Windows doesn't have standard directories where libraries 114 | live. That makes deploying libraries in Windows a lot more 115 | painful than open source developers on Unix-derivates generally 116 | realize. (It also makes library dependencies a lot worse in Windows.) 117 | 118 | There's also a common problem in Windows where a library was built 119 | against a different version of the runtime library, which causes 120 | link conflicts and confusion. Shipping the libs as headers means 121 | you normally just compile them straight into your project without 122 | making libraries, thus sidestepping that problem. 123 | 124 | Making them a single file makes it very easy to just 125 | drop them into a project that needs them. (Of course you can 126 | still put them in a proper shared library tree if you want.) 127 | 128 | Why not two files, one a header and one an implementation? 129 | The difference between 10 files and 9 files is not a big deal, 130 | but the difference between 2 files and 1 file is a big deal. 131 | You don't need to zip or tar the files up, you don't have to 132 | remember to attach *two* files, etc. 133 | 134 | #### Why "stb"? Is this something to do with Set-Top Boxes? 135 | 136 | No, they are just the initials for my name, Sean T. Barrett. 137 | This was not chosen out of egomania, but as a moderately sane 138 | way of namespacing the filenames and source function names. 139 | 140 | #### Will you add more image types to stb_image.h? 141 | 142 | No. As stb_image use has grown, it has become more important 143 | for us to focus on security of the codebase. Adding new image 144 | formats increases the amount of code we need to secure, so it 145 | is no longer worth adding new formats. 146 | 147 | #### Do you have any advice on how to create my own single-file library? 148 | 149 | Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt 150 | 151 | #### Why public domain? 152 | 153 | I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons. 154 | Some of them are listed here: 155 | https://github.com/nothings/stb/blob/master/docs/why_public_domain.md 156 | 157 | #### Why C? 158 | 159 | Primarily, because I use C, not C++. But it does also make it easier 160 | for other people to use them from other languages. 161 | 162 | #### Why not C99? stdint.h, declare-anywhere, etc. 163 | 164 | I still use MSVC 6 (1998) as my IDE because it has better human factors 165 | for me than later versions of MSVC. 166 | -------------------------------------------------------------------------------- /papers/format.md: -------------------------------------------------------------------------------- 1 | ─ │ ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼ 2 | # ---TRACKER'S FILE FORMAT SPECIFICATION--- 3 | 4 | this document should help you read the soundtracker file format. 5 | 6 | ## Header, 384 bytes 7 | ---- -0- -1- -2- -3- -4- -5- -6- -7- -8- -9- -A- -B- -C- -D- -E- -F- 8 | ┌───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┬───┬───┬───┬───┬───┐ 9 | 00│ T │ R │ A │ C │ K │ 8 │ B │ T │Version│Ins│Pat│Ord│Spd│Seq│Tmp│ 10 | ├───┴───┴───┴───┴───┴───┴───┴───┴───────┴───┴───┴───┴───┴───┴───┤ 11 | 10│ song name, 32 chars max, padded with 0x00s │ 12 | ├───┬───┬───────┬───┬───┬───────────────┬───────────────┬───┬───┤ 13 | 30│DFM│#CH│ flags │gvl│gpn│ PCM pointer │Comment pointer│DFA│Len│ 14 | ├───┴───┴───────┴───┴───┴───────────────┴───────────────┴───┴───┤ 15 | 40│ default volume per channel, 32 bytes │ 16 | ├───────────────────────────────────────────────────────────────┤ 17 | 60│ default panning per channel, 32 bytes │ 18 | ├───────────────────────────────────────────────────────────────┤ 19 | 80│ order list, 256 bytes │ 20 | ├───────────────────────────────────────────────────────────────┤ 21 | 180│ pointers to instruments, 4 bytes per pointer │ 22 | ├───────────────────────────────────────────────────────────────┤ 23 | xxxx│ pointers to macros or sequence tables (pre-r152), 4 bytes/ptr │ 24 | ├───────────────────────────────────────────────────────────────┤ 25 | xxxx│ pointers to patterns, 4 bytes per pointer │ 26 | └───────────────────────────────────────────────────────────────┘ 27 | Note: DFA: detune 28 | Flags: bit 0: NTSC 29 | bit 1: NTSC50 30 | bit 2: noise compatibility mode 31 | if set then noise frequency is divided by 4. 32 | 33 | ## Macros, new format 34 | read macros like this: 35 | 36 | 1. read the macro length (4 bytes). 37 | 2. read the "jump on release" position (4 bytes). 38 | * if this is -1, then it is "don't jump". 39 | 3. read the macro intended use (1 byte): 40 | - 0: generic 41 | - 1: shape 42 | - 2: pitch 43 | - 3: panning 44 | - 4: volume sweep 45 | - 5: other sweep 46 | 3. skip 7 bytes (reserved section). 47 | 4. read the type. then switch based on it: 48 | - 0: end of macro. 49 | - 1: set. read four bytes for value. 50 | - 2: wait. read four bytes for length. 51 | - 3: wait for release. 52 | - 4: loop. read the position (4 bytes). 53 | - 5: loop until release. read the position (4 bytes). 54 | - 6: add. read four bytes for value. 55 | - 7: subtract. read four bytes for value. 56 | * if bit 7 is set, then it means end of tick (ignored on wait). 57 | 58 | ## Sequence tables, legacy (pre-r152) format, 2048 bytes each 59 | ---- -0- -1- -2- -3- -4- -5- -6- -7- -8- -9- -A- -B- -C- -D- -E- -F- 60 | ┌───────────────────────────────────────────────────────────────┐ 61 | 00│ main sequence, 253 bytes │ 62 | ├───────────────────────────────────────────────────┬───┬───┬───┤ 63 | F0│ │Len│Lps│Rps│ 64 | └───────────────────────────────────────────────────┴───┴───┴───┘ 65 | 66 | read 8 times to get the tables in this order: 67 | - 0: volume 68 | - 1: cutoff 69 | - 2: resonance 70 | - 3: duty 71 | - 4: shape 72 | - 5: pitch 73 | - 6: fine pitch 74 | - 7: panning 75 | 76 | ## Instruments, new format, 96 bytes each 77 | ---- -0- -1- -2- -3- -4- -5- -6- -7- -8- -9- -A- -B- -C- -D- -E- -F- 78 | ┌───────────────────────────────────────────────────────────────┐ 79 | 00│ instrument name, 32 bytes, padded with 0x00s │ 80 | ├───┬───┬───────┬───────┬───────┬───────┬───┬───┬───┬───┬───┬───┤ 81 | 20│Ins│???│volumeM│cutoffM│resonaM│pitchM │???│Off│FPt│FPR│DFM│LFO│ 82 | ├───┼───┼───────┼───────┼───┬───┴───┬───┴───┼───┼───┴───┼───┼───┤ 83 | 30│Vol│Pit│PCMSlen│FilterH│Res│FTm│PCMSptr│PCMloop│Version│flg│RMF│ 84 | ├───┴───┼───────┼───────┼───┴───┬───┴───┬───┴───┼───────┼───┴───┤ 85 | 40│finepiM│shapeM │ dutyM │ panM │filterM│volswpM│frqswpM│cutswpM│ 86 | ├───────┼───────┴───────┴───────┴───────┴───────┴───────┴───────┤ 87 | 50│pcmposM│ r e s e r v e d │ 88 | └───────┴───────────────────────────────────────────────────────┘ 89 | 90 | NOTE: Instrument files start with a header which is 8 bytes long and reads "TRACKINS". 91 | Also, instrument files don't require the envelope IDs as they're saved along with the file. 92 | NOTE: If (DFM&8) then it's a PCM instrument. 93 | If (DFM&16) then enable ring modulation and LFO sets the ring modulation frequency. 94 | RMF sets the ring modulator flags: 95 | (RMF&7): waveform 96 | the rest of bits: duty 97 | Also, (DFM&128) will be used for the MSB of PCMSptr and (DFM&64) the MSB of PCMloop. 98 | Also, (DFM&32) enables looping. 99 | PCm: PCM multiplier. Max value is 127. 100 | PCm&128: MSB of length 101 | flg: other flags: 102 | bit 0: reset oscillator on new note 103 | bit 1: MSB of duty 104 | bit 2: reset filter on new note 105 | bit 3: reset RM oscillator on new note 106 | bit 4: sync modulation 107 | bit 5: PCM loop enabled 108 | bit 6-7: auto-cut 109 | 110 | ## Instruments, legacy (pre-r152) format, 64 bytes each 111 | ---- -0- -1- -2- -3- -4- -5- -6- -7- -8- -9- -A- -B- -C- -D- -E- -F- 112 | ┌───────────────────────────────────────────────────────────────┐ 113 | 00│ instrument name, 32 bytes, padded with 0x00s │ 114 | ├───┬───┬───┬───────────────────────────────┬───┬───┬───┬───┬───┤ 115 | 20│Ins│PCm│Env│vol cut res dty shp pit hpi pan│Off│FPt│FPR│DFM│LFO│ 116 | ├───┼───┼───┴───┬───────┬───┬───────┬───────┼───┼───┴───┼───┼───┤ 117 | 30│Vol│Pit│PCMSlen│FilterH│Res│PCMSptr│PCMloop│FTm│Version│flg│RMF│ 118 | └───┴───┴───────┴───────┴───┴───────┴───────┴───┴───────┴───┴───┘ 119 | 120 | NOTE: Instrument files start with a header which is 8 bytes long and reads "TRACKINS". 121 | Also, instrument files don't require the envelope IDs as they're saved along with the file. 122 | NOTE: If (DFM&8) then it's a PCM instrument. 123 | If (DFM&16) then enable ring modulation and LFO sets the ring modulation frequency. 124 | RMF sets the ring modulator flags: 125 | (RMF&7): waveform 126 | the rest of bits: duty 127 | Also, (DFM&128) will be used for the MSB of PCMSptr and (DFM&64) the MSB of PCMloop. 128 | Also, (DFM&32) enables looping. 129 | PCm: PCM multiplier. Max value is 127. 130 | PCm&128: MSB of length 131 | flg: other flags: 132 | bit 0: reset oscillator on new note 133 | bit 1: MSB of duty 134 | bit 2: reset filter on new note 135 | bit 3: reset RM oscillator on new note 136 | bit 4: sync modulation 137 | bit 5: PCM loop enabled 138 | bit 6-7: auto-cut 139 | 140 | ## Patterns, 16 byte header 141 | ---- -0- -1- -2- -3- -4- -5- -6- -7- -8- -9- -A- -B- -C- -D- -E- -F- 142 | ┌───────────────────────────────────────────────────────────────┐ 143 | 00│PID│ Length |Siz| Reserved, 11 bytes | 144 | ├───────────────────────────────────────────────────────────────┤ 145 | 10│ Pattern data │ 146 | └───────────────────────────────────────────────────────────────┘ 147 | 148 | Pattern data is organized like this: 149 | NOTE INST VOLU FXID FXVL 150 | NOTE: channel's note, high nibble is octave, low nibble: 151 | -0: no note 152 | -1-12: notes from C to B 153 | -13: note off 154 | -14: note fade 155 | -15: note cut 156 | INST: channel's instrument 157 | VOLU: channel's volume value: 158 | -0: nothing 159 | -1-63: panning (1-63) 160 | -64-127: volume, overrides instrument's default volume 161 | -128-137: fine volume slide up 162 | -138-147: fine volume slide down 163 | -160-175: volume slide up 164 | -176-183: volume slide down 165 | -184-193: pitch slide down 166 | -194-203: pitch slide up 167 | -204-213: portamento 168 | -214-223: vibrato depth 169 | -224-239: high offset 170 | -240: panning (0) 171 | -241: panning (64) 172 | -242-248: panning slide left (0-7)\*2 173 | -249-255: panning slide right (0-7)\*2 174 | FXID: channel's effect number 175 | FXVL: channel's effect value 176 | 177 | please note that the patterns are also compressed, 178 | in a way pretty similar to how ST3 does. 179 | 180 | in case you don't know how ST3 "packs" patterns, here it is: 181 | 1. read maskbyte in seek position 182 | 2. if (maskbyte==0) then row is done 183 | 3. channel=maskbyte%32 184 | 4. if (maskbyte&32) then read NOTE and INSTRUMENT 185 | 5. if (maskbyte&64) then read VOLUME 186 | 6. if (maskbyte&128) then read EFFECT and EFFECTVALUE 187 | 7. repeat until pattern is done or `seek>size` 188 | 189 | ## Sound effect structure (perhaps legacy, I don't remember) 190 | ---- -0- -1- -2- -3- -4- -5- -6- -7- -8- -9- -A- -B- -C- -D- -E- -F- 191 | ┌───────────────────────────────────────────────────────────────┐ 192 | 00│ Packed effect data | 193 | └───────────────────────────────────────────────────────────────┘ 194 | 195 | and this is how soundtracker packs sound effects: 196 | 1. read maskbyte in seek position 197 | 2. if (maskbyte==0) then effect is done 198 | 3. if (maskbyte&1) then PERIOD (hl) and VOLUME follows 199 | 4. if (maskbyte&2) then PAN follows 200 | 3. if (maskbyte&4) then DUTY and SHAPE follows 201 | 4. if (maskbyte&8) then RM, RMPERIOD (hl), RMSHAPE and RMDUTY follows 202 | 5. if (maskbyte&16) then PCMpos, PCMbound and PCMloop follow 203 | 6. if (maskbyte&32) then FILTERMODE, CUTOFF and RESONANCE follow 204 | -------------------------------------------------------------------------------- /papers/help.txt: -------------------------------------------------------------------------------- 1 | (100 columns) 2 | ---SOUNDTRACKER HELP--- 3 | table of contents: 4 | 1. what's soundtracker? 5 | 2. 6 | 3. user interface 7 | 3.1. header 8 | 3.2. pattern view 9 | 3.3. instrument view 10 | 3.4. song view 11 | 3.5. mixer view 12 | 3.6. diskop view 13 | 3.7. config view 14 | 4. reference 15 | 4.1. keyboard reference 16 | 4.2. effect reference 17 | 4.3. volume column reference 18 | 4.4. Zxx reference 19 | 4.5. compatibility reference 20 | 4.6. in-depth effect information 21 | 4.7. chip's technical information 22 | 23 | 1. what's soundtracker? 24 | soundtracker (not going to be the final name!) is a tool for creating 8-bit chiptunes 25 | using the basis of trackers. 26 | Unlike similar 8-bit trackers, this tracker is designed to be more IT-friendly. 27 | 28 | 2. 29 | 30 | 3. user interface 31 | 32 | 3.1. 33 | 34 | 3.2. pattern view 35 | use left and right to move the cursor 36 | use up and down to scroll between rows 37 | use the keyboard like a piano to put a note (volume and instrument values are also set) 38 | you can set the current effect by going there 39 | NOTE|INSTRUMENT|VOLUME|EFFECT 40 | ... .. ... ... -> C-5 01 v40 G00 41 | 42 | 3.3. instrument view 43 | (I'll write more later) 44 | -INSXX^v: current instrument to be edited 45 | -Save: saves this instrument 46 | -Load: load an instrument 47 | -RelNote: sets relative note for this instrument 48 | -Name: sets the name of this instrument (max 32 chars) 49 | -Filter: sets the filter mode for this channel (won't be active if cutoff envelope is not active) 50 | 51 | 52 | 53 | (Sequence Editor) 54 | -ON/NO: toggles if this envelope will be used 55 | -SeqXX^v: sets the sequence to be used 56 | -NF: finds the next free envelope and assigns it to this instrument 57 | -X: clears the current envelope (no warnings!) 58 | -Loop: loop point for this sequence 59 | -Release: release point for this sequence 60 | -Length: length of this sequence 61 | 62 | 4. reference 63 | 64 | 4.1. keyboard reference 65 | 66 | 4.2. effect reference 67 | The effect set used by soundtracker is the same used in Impulse Tracker. 68 | [effect] [action] 69 | Axx Sets the speed to XX. Effect is ignored if A00. 70 | Bxx Jumps to pattern XX. 71 | Cxx Pattern break. Jumps to next pattern at position XX. 72 | Dxx Volume slide. 0X for a down slide and X0 for a up slide. 73 | Exx Pitch slide down. 74 | Fxx Pitch slide up. 75 | Gxx Tone portamento. XX is speed. 76 | Hxy Vibrato. 77 | Ixy Tremor. 78 | Jxy Arpeggio. 79 | Kxx Volume slide + Vibrato. 80 | Lxx Volume slide + Tone Portamento. 81 | Mxx Sets channel volume. 82 | Nxx Channel volume slide. 83 | Oxx Start all envelopes at position XX. 84 | Pxx Panning slide. 0X for a right slide and X0 for a left slide. 85 | Qxy Retriggers all envelopes. 86 | Rxy Tremolo. 87 | Sxy Special commands. 88 | Txx Set tempo. 89 | Uxy Fine vibrato. 90 | Vxx Set master volume. 91 | Wxx Master volume slide. 92 | Xxx Set panning. 93 | Yxy Panning tremolo. 94 | Zxx Tracker command. 95 | 96 | 4.3. volume column reference 97 | 98 | 4.4. Zxx reference 99 | *** NOTE: This is not implemented yet! *** 100 | The Zxx effect allows controlling the chip's registers and some tracker variables directly. 101 | Values from 00-7f change the current variable's value. 102 | Values from 80-ff allow setting up the current variable. 103 | [value] [action] 104 | $80 Do nothing. 105 | $81 Enable smooth value changes. 106 | $82 Disable smooth value changes. 107 | $90-$97 Enable envelopes for this channel. 108 | $98-$9f Disable envelopes for this channel. 109 | $a0-$a7 Set filter mode for unfiltered notes on this channel. 110 | $a8-$af Force filters on this channel. 111 | $b0 Cutoff handling. 112 | $b1 Resonance handling. 113 | $b2 Toggle forcing ring modulator. 114 | $b3 Ring modulator (note) handling. 115 | $b4 Ring modulator (sweep) handling. 116 | $b5 Ring modulator (relative note) handling. 117 | $b6 Ring modulator (multiplier) handling. 118 | $b7 Ring modulator pulse width handling. 119 | $b8-$bf Set ring modulator shape. 120 | $c0 Pulse width handling. 121 | $c1 Pulse width (sweep) handling. 122 | $c8-$cf Force a shape under non-shaped notes on this channel. 123 | $d0 Force ADSR volume on this channel. 124 | $d1 Force ADSR cutoff on this channel. 125 | $d2-$d5 Volume ADSR parameter handling. 126 | $d6-$d9 Cutoff ADSR parameter handling. 127 | $e0-$e7 Toggle forcing another envelope on this channel. 128 | $e8-$ef Forced envelope handling. 129 | $f0 Force oscillator. 130 | $f1 Force PCM sampler. 131 | $f2 Toggle forcing oscillator sync (self). Use RM parameters. 132 | $f3 Toggle forcing oscillator sync (send). 133 | $f4 Local mode for switches. 134 | $f5 Global mode for switches. 135 | $fb Reset registers. 136 | $fc Reset oscillator. 137 | $fd Reset filter. 138 | $fe Reset RM oscillator. 139 | $ff Reset channel. 140 | 141 | 4.5. compatibility reference 142 | -Gxx behavior: 143 | -IT: share Gxx memory with Exx and Fxx 144 | -original/PT/XM/S3M: don't share memory 145 | -Exx/Fxx memory: 146 | -IT: there is E/F memory 147 | -original/PT/XM/S3M: nothing 148 | -Arpeggios: 149 | -original/IT/PT/S3M: normal arpeggios 150 | -XM: reversed arpeggios, and unexpected behavior at speed>16 151 | -Effect values: 152 | -original/IT/PT/XM: zero values don't use the previous effect's value 153 | -S3M: zero values use the previous effect's value 154 | -Note range: 155 | -original/IT/XM/S3M: C-0 to B-9 156 | -PT: C-3 to B-6 157 | -Retrigger with RVCT value 0: 158 | -original/IT/PT/S3M: no change 159 | -XM: previous value 160 | -Tremor: 161 | -original/PT/S3M/XM: clean tremor 162 | -IT: buggy tremor 163 | -Instrument without note: 164 | -original/S3M/XM: reset volume, nothing else 165 | -IT: retrigger note with new instrument 166 | -PT: change instrument but do not retrigger note 167 | -Dxx memory: 168 | -PT: no Dxx memory 169 | -original/IT/S3M/XM: Dxx memory present 170 | 171 | 4.6. in-depth effect information 172 | this is how soundtracker processes effects. 173 | -Axx: Set Speed: sets the speed ONLY in the first tick. 174 | -Bxx: Jump to Row: jumps to order XX after this row. 175 | -Cxx: Jump to Next: jumps to next order after this row, starting at position XX. 176 | -Dxx: Volume Slide: slides the volume either up or down per tick. 177 | -if 0x, then volume-=x; otherwise volume+=x 178 | -if Fx, then previous procedure applies only in the first tick 179 | -Exx: Pitch Slide Down: slides the pitch down per tick. 180 | -if Ex, changes are 4 times more precise 181 | -if Fx, slide is only done in first tick 182 | -Fxx: Pitch Slide Up: same as above but goes up. 183 | -Gxx: Tone Portamento: slides to new note with speed xx. 184 | -if this effect is present do not reset envelopes. 185 | -Hxy: Vibrato: executes a vibrato using a sine wave at speed X and depth Y. 186 | -original pitch is preserved, though. 187 | -Ixy: Tremor: note on for X ticks, and off for Y ticks. 188 | -Jxy: Arpeggio: 189 | (i'll write more later) 190 | 191 | 4.7. chip's technical information 192 | the chip emulated in the tracker is a custom chip. here's its description. 193 | -8 channels 194 | -2 modes per channel: OSCILLATOR, and PCM SAMPLES. 195 | -filters (low, high and band) in all modes. 196 | -ring modulation in ALL modes! 197 | (i'll write more later, sorry) 198 | -------------------------------------------------------------------------------- /papers/ibm-plex-license.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font creation 14 | efforts of academic and linguistic communities, and to provide a free and 15 | open framework in which fonts may be shared and improved in partnership 16 | with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply 25 | to any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software components as 36 | distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, deleting, 39 | or substituting -- in part or in whole -- any of the components of the 40 | Original Version, by changing formats or by porting the Font Software to a 41 | new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 49 | redistribute, and sell modified and unmodified copies of the Font 50 | Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, 53 | in Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the corresponding 64 | Copyright Holder. This restriction only applies to the primary font name as 65 | presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created 77 | using the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /papers/info.txt: -------------------------------------------------------------------------------- 1 | This is a chiptune tracker. 2 | 3 | ## Features. 4 | - You can work with up to 4 chips at once for a total of 32 channels. 5 | - You can import IT/S3M/MOD files directly into the program. 6 | 7 | ## Tracker's Specifications. 8 | This is a format specification! 9 | 10 | 11 | bytable[8][256][256], allows for setting up the envelope tables, up to 512kb 12 | 13 | bytable[table][indextab][position], where 14 | 15 | table=0-7, 0=volume, 1=pitch, 2=pitch*256, 3=cutoff, 4=resonance-filter-mode, 5=shape, 6=duty 16 | 17 | position=0-255, 0-252=envelope, 253=length, 254=loop-position, 255=release-position 18 | 19 | 20 | pat[256][256][8][5], pattern table, 2560kb 21 | 22 | pat[patid][patpos][channel][bytepos], where 23 | 24 | patid=0-255 25 | 26 | patpos=0-255 27 | 28 | channel=0-7 29 | 30 | bytepos=0-4, 0=note, 1=instrument-reverse, 2=volume-effect, 3=effect, 4=effect-value 31 | 32 | ## Playback. 33 | Playback is done like this: 34 | 35 | ###(Next Tick) 36 | 1. Update all envelope positions. 37 | 2. Apply effects if required. 38 | 39 | ###(Next Row) 40 | 1. Reset the tick counter. 41 | 2. If there is a new note: 42 | 1. Reset all the envelopes, unless there's a Gxx effect in such row. 43 | 2. Change the note value, unless there's a Gxx effect in such row. 44 | 3. If there is a new volume value: 45 | 1. Set note volume. 46 | 4. If there is any effect in the effect column: 47 | 1. Process such effect. 48 | 49 | ###(Next Effect) 50 | - Axx: Change the song speed. 51 | - Bxx: Stop this pattern and go to pattern XX. 52 | - Cxx: Skip to next pattern. 53 | - Dxx: Fade in/out the note's volume, while not in Next Row. 54 | - Exx: Decrease the frequency value and set the portamento memory value for further E00s. 55 | - Fxx: Same as Exx but increase it instead. 56 | - Gxx: Portamento with a note. 57 | - Hxy: Apply vibrato effects. 58 | - Ixy: Apply tremor effects. 59 | - Jxy: Apply arpeggio effects. 60 | - Kxy: Slide+Vibrato. 61 | - Lxy: Slide+Porta. 62 | - Mxx: Set channel volume. 63 | - Nxx: Channel volume slide. 64 | - Oxx: Sets the position for ALL envelopes to XX. 65 | - Pxx: Panning slides. 66 | - Qxy: Apply retrigger effects. 67 | - Rxy: Apply tremolo effects. 68 | - Sxy: Apply any special commands if required. 69 | - Txx: Set tempo (not supported). 70 | - Uxy: Apply fine vibrato effects. 71 | - Vxx: 72 | - Wxx: 73 | - Xxx: Set channel panning. 74 | - Yxy: Panning tremolo. 75 | - Zxx: Apply MIDI macro. However they are not stored in the resulting file. 76 | 77 | ## File Format's Specifications. 78 | check format.md. 79 | 80 | ## Note Periods. 81 | no longer used. 82 | 83 | note| freq | period 84 | ----|-------|------ 85 | C-0 | 16,3525| 18346 86 | C#0 | 17,325 | 17316 87 | D-0 | 18,355 | 16344 88 | D#0 | 19,445 | 15428 89 | E-0 | 20,6025| 14561 90 | F-0 | 21,8275| 13744 91 | F#0 | 23,125 | 12973 92 | G-0 | 24,5 | 12245 93 | G#0 | 25,9575| 11557 94 | A-0 | 27,5 | 10909 95 | A#0 | 29,135 | 10297 96 | B-0 | 30,8675| 9719 97 | 98 | # <~> 99 | -------------------------------------------------------------------------------- /papers/ohplease.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tildearrow/soundtracker/4240845b5e00510128240222ce66d87fbe71daf7/papers/ohplease.png -------------------------------------------------------------------------------- /papers/ssformat.md: -------------------------------------------------------------------------------- 1 | # soundchip dump format 2 | 3 | the format is very similar to the one used in family BASIC for the play command. 4 | 5 | every property may take a value from a Lisp expression if it contains parentheses (e.g. `f(* 256 4)`. 6 | 7 | ## proper format 8 | 9 | + $: select channel. 10 | + f: set frequency. 4 hexadecimal digits as argument. 11 | + V: volume (0-127). 12 | - P: panning (char). 13 | + Y: duty (0-127). 14 | + S: shape (0-7). 15 | + c: cutoff (short). 16 | + r: resonance (char). 17 | + I: filter flag (0-7). 18 | - O: base octave (0-9). 19 | - e: reset channel. 20 | - g: reset filter. 21 | - h: set ring/sync flags. 22 | + R: finish tick, then rest for a number of ticks. 23 | + ;: finish tick. 24 | 25 | - [#]CDEFGAB: notes. appending a number will add that number of octaves to the base one. 26 | 27 | - M: enable sweep units (0-7). 28 | - v: configure volume sweep. 29 | - l: configure filter sweep. 30 | - k: configure freq sweep. 31 | 32 | - p: enable/configure PCM. 33 | 34 | - T: set sync period. 35 | 36 | - /: go to line. 37 | 38 | - =: execute Lisp expression/ignore value. 39 | - ?: go to line if expression is true. 40 | 41 | ## sweep config format 42 | 43 | - frequency and filter: 44 | - SSSSaabb 45 | - S: speed. a: amount/dir. b: bound. 46 | - volume: 47 | - SSSSaabb 48 | - S: speed. a: amount/dir/loop/loopi. b: bound. 49 | 50 | ## Lisp symbols 51 | 52 | the SSLisp interpreter is minimalistic, and therefore only supports one or two-letter symbol names. 53 | 54 | the following symbols are set by ssinter: 55 | 56 | - `xf`: frequency 57 | - `xv`: volume 58 | - `xc`: cutoff 59 | 60 | - `xl`: current frame 61 | 62 | ## Lisp functions 63 | 64 | - `(note x)`: calculate note frequency for `x` 65 | - `(trans x)`: calculate transpose for `x` semitones 66 | -------------------------------------------------------------------------------- /papers/ssiasm.md: -------------------------------------------------------------------------------- 1 | # soundtracker instrument assembly 2 | 3 | this is an advanced mode in where a custom program runs to handle the instrument. 4 | 5 | it is based on a stack machine. 6 | 7 | ## details 8 | 9 | - fully stack-oriented instruction set 10 | - stack size is 8 items 11 | - 16-item scratchpad 12 | - works on 32-bit integer and 16-bit fixed-point 13 | - instructions are 64-bit but packed for storage 14 | 15 | ## instruction set 16 | 17 | opcode | sym | desc 18 | -------|-----|---------------------- 19 | add | + | add two items 20 | sub | - | subtract two items 21 | mul | * | multiply two items 22 | div | / | divide two items, divisor first 23 | mod | % | remainder of division, divisor first 24 | mulfi | *fi | multiply int with fixed 25 | mulff | *ff | multiply fixed with fixed 26 | divfi | /fi | divide int with fixed 27 | divff | /ff | divide fixed with fixed 28 | and | & | bitwise and 29 | or | | | bitwise or 30 | xor | ^ | bitwise xor 31 | not | ~ | bitwise not 32 | neg | - | negative 33 | sal | << | shift left 34 | sar | >> | shift right 35 | rol | <<. | rotate left 36 | ror | >>. | rotate right 37 | push | < | push value from scratchpad 38 | pushc | < | push value from soundchip 39 | pushi | < | push value immediate 40 | pop | > | pop value to scratchpad 41 | popc | > | pop value to soundchip 42 | drop | > | pop value to nowhere 43 | dup | # | duplicate value 44 | clr | . | empty stack 45 | bra | ^^^ | branch always 46 | beq | ^== | branch if zero 47 | bne | ^!= | branch if not zero 48 | bpl | ^> | branch if positive 49 | bmi | ^< | branch if negative 50 | off | $ | select channel by offset 51 | chan | $ | select channel by index 52 | wait | --- | wait ticks 53 | ret | === | stop execution 54 | brk | !!! | break here 55 | nop | ... | no operation 56 | 57 | ## soundchip variables 58 | 59 | addr | value 60 | -----|----------- 61 | $00 | channel variables (32) 62 | $20 | note frequency 63 | $21 | note in fixed 64 | $22 | unprocessed note 65 | $23 | unprocessed note in fixed 66 | $24 | volume 67 | $25 | unprocessed volume 68 | $26 | panning 69 | $27 | unprocessed panning 70 | $28 | note on 71 | $29 | current tick in pattern 72 | $2a | current tick in song 73 | 74 | # screw this document 75 | 76 | yep, this is a lot of nonsense I wrote the other day. -------------------------------------------------------------------------------- /papers/tooltips.txt: -------------------------------------------------------------------------------- 1 | Speed (ticks/row) 2 | Playback position 3 | Pattern number for this order 4 | Disk operations 5 | Pattern view 6 | Instrument editor 7 | Song view 8 | Mixer 9 | Settings 10 | Help 11 | Enable pattern editing 12 | Play from cursor 13 | Play from pattern start 14 | Stop 15 | Step playback 16 | Current row 17 | Current pattern 18 | Current tick 19 | Current speed 20 | Orders 21 | Note 22 | Instrument 23 | Volume 24 | Effect 25 | Row number 26 | About 27 | Instrument number 28 | Save instrument 29 | Load instrument 30 | Disable/Enable envelope 31 | Volume envelope 32 | Cutoff envelope 33 | Resonance envelope 34 | Duty envelope 35 | Shape envelope 36 | Pitch envelope 37 | Fine pitch envelope 38 | Panning envelope 39 | Sequence number 40 | Use next free sequence 41 | Clear envelope 42 | Hex mode 43 | Loop position (FF=disabled) 44 | Release position (FF=disabled) 45 | Envelope length 46 | Relative note (transposition) 47 | Filter mode (current: %s) 48 | Low pass 49 | High pass 50 | Band pass 51 | disabled 52 | lowpass 53 | highpass 54 | bandpass 55 | notch 56 | allpass 57 | low+band 58 | high+band 59 | PCM sample mode 60 | Sample offset 61 | Sample length 62 | Sample loop point 63 | PCM speed multiplier 64 | Maximum cutoff frequency 65 | Ring modulation 66 | Ring modulator frequency 67 | Ring modulator shape 68 | Ring modulator duty 69 | Oscillator sync 70 | Reset oscillator on new note 71 | Reset filter on new note 72 | Reset ringmod oscillator on new note 73 | Default volume 74 | Default song speed (ticks/row) 75 | Channel number 76 | Rows per beat 77 | Rows per bar 78 | Song detune (%s) 79 | Song length 80 | Default song tempo 81 | Frequency 82 | Volume 83 | Panning 84 | Duty 85 | Cutoff 86 | Resonance 87 | Filter mode 88 | Shape 89 | PCM 90 | Ring modulation 91 | Oscillator sync 92 | PCM offset 93 | PCM bound 94 | PCM start 95 | PCM multiplier 96 | RM frequency 97 | RM shape 98 | RM duty 99 | Playback mode 100 | Normal playback mode 101 | Impulse Tracker-compatible mode 102 | FastTracker 2-compatible mode 103 | ProTracker-compatible mode 104 | Scream Tracker 3-compatible mode 105 | Share Gxx memory with Exx and Fxx 106 | Double filter frequency limit 107 | Sliding mode 108 | Linear slides 109 | 300KHz-periods mode 110 | Amiga slides 111 | Default channel volume 112 | Default channel panning 113 | Duty (%s%) 114 | Square 115 | Saw 116 | Sine 117 | Triangle 118 | Noise 119 | Duty noise 120 | Shape 6 121 | Shape 7 122 | Frequency (%sHz) 123 | Cutoff 124 | Instrument number 125 | Channel volume 126 | Volume envelope position 127 | Cutoff envelope position 128 | Resonance envelope position 129 | Duty envelope position 130 | Shape envelope position 131 | Pitch envelope position 132 | Fine pitch envelope position 133 | Panning envelope position 134 | Current note (%.2f, %.1fHz) 135 | Portamento note (%.2f, %.1fHz) 136 | New file 137 | Load file 138 | Save file 139 | Import ProTracker module 140 | Import Scream Tracker 3 module 141 | Import Impulse Tracker module 142 | Import FastTracker 2 module 143 | File name -------------------------------------------------------------------------------- /papers/unifont-license.txt: -------------------------------------------------------------------------------- 1 | LICENSE 2 | ------- 3 | The source code for everything except the compiled fonts in this current 4 | release is licensed as follows: 5 | 6 | License for this current distribution of program source 7 | files (i.e., everything except the fonts) is released under 8 | the terms of the GNU General Public License version 2, 9 | or (at your option) a later version. 10 | 11 | See the section below for a copy of the GNU General Public License 12 | version 2. 13 | 14 | The license for the compiled fonts is covered by the above GPL terms 15 | with the GNU font embedding exception, as follows: 16 | 17 | As a special exception, if you create a document which uses this font, 18 | and embed this font or unaltered portions of this font into the document, 19 | this font does not by itself cause the resulting document to be covered 20 | by the GNU General Public License. This exception does not however 21 | invalidate any other reasons why the document might be covered by the 22 | GNU General Public License. If you modify this font, you may extend 23 | this exception to your version of the font, but you are not obligated 24 | to do so. If you do not wish to do so, delete this exception statement 25 | from your version. 26 | 27 | See "http://www.gnu.org/licenses/gpl-faq.html#FontException" for more details. 28 | 29 | 30 | GPL VERSION 2 31 | ------------- 32 | 33 | GNU GENERAL PUBLIC LICENSE 34 | Version 2, June 1991 35 | 36 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 37 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 38 | Everyone is permitted to copy and distribute verbatim copies 39 | of this license document, but changing it is not allowed. 40 | 41 | Preamble 42 | 43 | The licenses for most software are designed to take away your 44 | freedom to share and change it. By contrast, the GNU General Public 45 | License is intended to guarantee your freedom to share and change free 46 | software--to make sure the software is free for all its users. This 47 | General Public License applies to most of the Free Software 48 | Foundation's software and to any other program whose authors commit to 49 | using it. (Some other Free Software Foundation software is covered by 50 | the GNU Lesser General Public License instead.) You can apply it to 51 | your programs, too. 52 | 53 | When we speak of free software, we are referring to freedom, not 54 | price. Our General Public Licenses are designed to make sure that you 55 | have the freedom to distribute copies of free software (and charge for 56 | this service if you wish), that you receive source code or can get it 57 | if you want it, that you can change the software or use pieces of it 58 | in new free programs; and that you know you can do these things. 59 | 60 | To protect your rights, we need to make restrictions that forbid 61 | anyone to deny you these rights or to ask you to surrender the rights. 62 | These restrictions translate to certain responsibilities for you if you 63 | distribute copies of the software, or if you modify it. 64 | 65 | For example, if you distribute copies of such a program, whether 66 | gratis or for a fee, you must give the recipients all the rights that 67 | you have. You must make sure that they, too, receive or can get the 68 | source code. And you must show them these terms so they know their 69 | rights. 70 | 71 | We protect your rights with two steps: (1) copyright the software, and 72 | (2) offer you this license which gives you legal permission to copy, 73 | distribute and/or modify the software. 74 | 75 | Also, for each author's protection and ours, we want to make certain 76 | that everyone understands that there is no warranty for this free 77 | software. If the software is modified by someone else and passed on, we 78 | want its recipients to know that what they have is not the original, so 79 | that any problems introduced by others will not reflect on the original 80 | authors' reputations. 81 | 82 | Finally, any free program is threatened constantly by software 83 | patents. We wish to avoid the danger that redistributors of a free 84 | program will individually obtain patent licenses, in effect making the 85 | program proprietary. To prevent this, we have made it clear that any 86 | patent must be licensed for everyone's free use or not licensed at all. 87 | 88 | The precise terms and conditions for copying, distribution and 89 | modification follow. 90 | 91 | GNU GENERAL PUBLIC LICENSE 92 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 93 | 94 | 0. This License applies to any program or other work which contains 95 | a notice placed by the copyright holder saying it may be distributed 96 | under the terms of this General Public License. The "Program", below, 97 | refers to any such program or work, and a "work based on the Program" 98 | means either the Program or any derivative work under copyright law: 99 | that is to say, a work containing the Program or a portion of it, 100 | either verbatim or with modifications and/or translated into another 101 | language. (Hereinafter, translation is included without limitation in 102 | the term "modification".) Each licensee is addressed as "you". 103 | 104 | Activities other than copying, distribution and modification are not 105 | covered by this License; they are outside its scope. The act of 106 | running the Program is not restricted, and the output from the Program 107 | is covered only if its contents constitute a work based on the 108 | Program (independent of having been made by running the Program). 109 | Whether that is true depends on what the Program does. 110 | 111 | 1. You may copy and distribute verbatim copies of the Program's 112 | source code as you receive it, in any medium, provided that you 113 | conspicuously and appropriately publish on each copy an appropriate 114 | copyright notice and disclaimer of warranty; keep intact all the 115 | notices that refer to this License and to the absence of any warranty; 116 | and give any other recipients of the Program a copy of this License 117 | along with the Program. 118 | 119 | You may charge a fee for the physical act of transferring a copy, and 120 | you may at your option offer warranty protection in exchange for a fee. 121 | 122 | 2. You may modify your copy or copies of the Program or any portion 123 | of it, thus forming a work based on the Program, and copy and 124 | distribute such modifications or work under the terms of Section 1 125 | above, provided that you also meet all of these conditions: 126 | 127 | a) You must cause the modified files to carry prominent notices 128 | stating that you changed the files and the date of any change. 129 | 130 | b) You must cause any work that you distribute or publish, that in 131 | whole or in part contains or is derived from the Program or any 132 | part thereof, to be licensed as a whole at no charge to all third 133 | parties under the terms of this License. 134 | 135 | c) If the modified program normally reads commands interactively 136 | when run, you must cause it, when started running for such 137 | interactive use in the most ordinary way, to print or display an 138 | announcement including an appropriate copyright notice and a 139 | notice that there is no warranty (or else, saying that you provide 140 | a warranty) and that users may redistribute the program under 141 | these conditions, and telling the user how to view a copy of this 142 | License. (Exception: if the Program itself is interactive but 143 | does not normally print such an announcement, your work based on 144 | the Program is not required to print an announcement.) 145 | 146 | These requirements apply to the modified work as a whole. If 147 | identifiable sections of that work are not derived from the Program, 148 | and can be reasonably considered independent and separate works in 149 | themselves, then this License, and its terms, do not apply to those 150 | sections when you distribute them as separate works. But when you 151 | distribute the same sections as part of a whole which is a work based 152 | on the Program, the distribution of the whole must be on the terms of 153 | this License, whose permissions for other licensees extend to the 154 | entire whole, and thus to each and every part regardless of who wrote it. 155 | 156 | Thus, it is not the intent of this section to claim rights or contest 157 | your rights to work written entirely by you; rather, the intent is to 158 | exercise the right to control the distribution of derivative or 159 | collective works based on the Program. 160 | 161 | In addition, mere aggregation of another work not based on the Program 162 | with the Program (or with a work based on the Program) on a volume of 163 | a storage or distribution medium does not bring the other work under 164 | the scope of this License. 165 | 166 | 3. You may copy and distribute the Program (or a work based on it, 167 | under Section 2) in object code or executable form under the terms of 168 | Sections 1 and 2 above provided that you also do one of the following: 169 | 170 | a) Accompany it with the complete corresponding machine-readable 171 | source code, which must be distributed under the terms of Sections 172 | 1 and 2 above on a medium customarily used for software interchange; or, 173 | 174 | b) Accompany it with a written offer, valid for at least three 175 | years, to give any third party, for a charge no more than your 176 | cost of physically performing source distribution, a complete 177 | machine-readable copy of the corresponding source code, to be 178 | distributed under the terms of Sections 1 and 2 above on a medium 179 | customarily used for software interchange; or, 180 | 181 | c) Accompany it with the information you received as to the offer 182 | to distribute corresponding source code. (This alternative is 183 | allowed only for noncommercial distribution and only if you 184 | received the program in object code or executable form with such 185 | an offer, in accord with Subsection b above.) 186 | 187 | The source code for a work means the preferred form of the work for 188 | making modifications to it. For an executable work, complete source 189 | code means all the source code for all modules it contains, plus any 190 | associated interface definition files, plus the scripts used to 191 | control compilation and installation of the executable. However, as a 192 | special exception, the source code distributed need not include 193 | anything that is normally distributed (in either source or binary 194 | form) with the major components (compiler, kernel, and so on) of the 195 | operating system on which the executable runs, unless that component 196 | itself accompanies the executable. 197 | 198 | If distribution of executable or object code is made by offering 199 | access to copy from a designated place, then offering equivalent 200 | access to copy the source code from the same place counts as 201 | distribution of the source code, even though third parties are not 202 | compelled to copy the source along with the object code. 203 | 204 | 4. You may not copy, modify, sublicense, or distribute the Program 205 | except as expressly provided under this License. Any attempt 206 | otherwise to copy, modify, sublicense or distribute the Program is 207 | void, and will automatically terminate your rights under this License. 208 | However, parties who have received copies, or rights, from you under 209 | this License will not have their licenses terminated so long as such 210 | parties remain in full compliance. 211 | 212 | 5. You are not required to accept this License, since you have not 213 | signed it. However, nothing else grants you permission to modify or 214 | distribute the Program or its derivative works. These actions are 215 | prohibited by law if you do not accept this License. Therefore, by 216 | modifying or distributing the Program (or any work based on the 217 | Program), you indicate your acceptance of this License to do so, and 218 | all its terms and conditions for copying, distributing or modifying 219 | the Program or works based on it. 220 | 221 | 6. Each time you redistribute the Program (or any work based on the 222 | Program), the recipient automatically receives a license from the 223 | original licensor to copy, distribute or modify the Program subject to 224 | these terms and conditions. You may not impose any further 225 | restrictions on the recipients' exercise of the rights granted herein. 226 | You are not responsible for enforcing compliance by third parties to 227 | this License. 228 | 229 | 7. If, as a consequence of a court judgment or allegation of patent 230 | infringement or for any other reason (not limited to patent issues), 231 | conditions are imposed on you (whether by court order, agreement or 232 | otherwise) that contradict the conditions of this License, they do not 233 | excuse you from the conditions of this License. If you cannot 234 | distribute so as to satisfy simultaneously your obligations under this 235 | License and any other pertinent obligations, then as a consequence you 236 | may not distribute the Program at all. For example, if a patent 237 | license would not permit royalty-free redistribution of the Program by 238 | all those who receive copies directly or indirectly through you, then 239 | the only way you could satisfy both it and this License would be to 240 | refrain entirely from distribution of the Program. 241 | 242 | If any portion of this section is held invalid or unenforceable under 243 | any particular circumstance, the balance of the section is intended to 244 | apply and the section as a whole is intended to apply in other 245 | circumstances. 246 | 247 | It is not the purpose of this section to induce you to infringe any 248 | patents or other property right claims or to contest validity of any 249 | such claims; this section has the sole purpose of protecting the 250 | integrity of the free software distribution system, which is 251 | implemented by public license practices. Many people have made 252 | generous contributions to the wide range of software distributed 253 | through that system in reliance on consistent application of that 254 | system; it is up to the author/donor to decide if he or she is willing 255 | to distribute software through any other system and a licensee cannot 256 | impose that choice. 257 | 258 | This section is intended to make thoroughly clear what is believed to 259 | be a consequence of the rest of this License. 260 | 261 | 8. If the distribution and/or use of the Program is restricted in 262 | certain countries either by patents or by copyrighted interfaces, the 263 | original copyright holder who places the Program under this License 264 | may add an explicit geographical distribution limitation excluding 265 | those countries, so that distribution is permitted only in or among 266 | countries not thus excluded. In such case, this License incorporates 267 | the limitation as if written in the body of this License. 268 | 269 | 9. The Free Software Foundation may publish revised and/or new versions 270 | of the General Public License from time to time. Such new versions will 271 | be similar in spirit to the present version, but may differ in detail to 272 | address new problems or concerns. 273 | 274 | Each version is given a distinguishing version number. If the Program 275 | specifies a version number of this License which applies to it and "any 276 | later version", you have the option of following the terms and conditions 277 | either of that version or of any later version published by the Free 278 | Software Foundation. If the Program does not specify a version number of 279 | this License, you may choose any version ever published by the Free Software 280 | Foundation. 281 | 282 | 10. If you wish to incorporate parts of the Program into other free 283 | programs whose distribution conditions are different, write to the author 284 | to ask for permission. For software which is copyrighted by the Free 285 | Software Foundation, write to the Free Software Foundation; we sometimes 286 | make exceptions for this. Our decision will be guided by the two goals 287 | of preserving the free status of all derivatives of our free software and 288 | of promoting the sharing and reuse of software generally. 289 | 290 | NO WARRANTY 291 | 292 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 293 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 294 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 295 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 296 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 297 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 298 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 299 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 300 | REPAIR OR CORRECTION. 301 | 302 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 303 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 304 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 305 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 306 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 307 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 308 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 309 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 310 | POSSIBILITY OF SUCH DAMAGES. 311 | 312 | END OF TERMS AND CONDITIONS 313 | 314 | How to Apply These Terms to Your New Programs 315 | 316 | If you develop a new program, and you want it to be of the greatest 317 | possible use to the public, the best way to achieve this is to make it 318 | free software which everyone can redistribute and change under these terms. 319 | 320 | To do so, attach the following notices to the program. It is safest 321 | to attach them to the start of each source file to most effectively 322 | convey the exclusion of warranty; and each file should have at least 323 | the "copyright" line and a pointer to where the full notice is found. 324 | 325 | 326 | Copyright (C) 327 | 328 | This program is free software; you can redistribute it and/or modify 329 | it under the terms of the GNU General Public License as published by 330 | the Free Software Foundation; either version 2 of the License, or 331 | (at your option) any later version. 332 | 333 | This program is distributed in the hope that it will be useful, 334 | but WITHOUT ANY WARRANTY; without even the implied warranty of 335 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 336 | GNU General Public License for more details. 337 | 338 | You should have received a copy of the GNU General Public License along 339 | with this program; if not, write to the Free Software Foundation, Inc., 340 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 341 | 342 | Also add information on how to contact you by electronic and paper mail. 343 | 344 | If the program is interactive, make it output a short notice like this 345 | when it starts in an interactive mode: 346 | 347 | Gnomovision version 69, Copyright (C) year name of author 348 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 349 | This is free software, and you are welcome to redistribute it 350 | under certain conditions; type `show c' for details. 351 | 352 | The hypothetical commands `show w' and `show c' should show the appropriate 353 | parts of the General Public License. Of course, the commands you use may 354 | be called something other than `show w' and `show c'; they could even be 355 | mouse-clicks or menu items--whatever suits your program. 356 | 357 | You should also get your employer (if you work as a programmer) or your 358 | school, if any, to sign a "copyright disclaimer" for the program, if 359 | necessary. Here is a sample; alter the names: 360 | 361 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 362 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 363 | 364 | , 1 April 1989 365 | Ty Coon, President of Vice 366 | 367 | This General Public License does not permit incorporating your program into 368 | proprietary programs. If your program is a subroutine library, you may 369 | consider it more useful to permit linking proprietary applications with the 370 | library. If this is what you want to do, use the GNU Lesser General 371 | Public License instead of this License. 372 | -------------------------------------------------------------------------------- /soundtracker.kdev4: -------------------------------------------------------------------------------- 1 | [Project] 2 | CreatedFrom=CMakeLists.txt 3 | Manager=KDevCMakeManager 4 | Name=soundtracker 5 | -------------------------------------------------------------------------------- /src/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := main 6 | 7 | SDL_PATH := ../SDL 8 | 9 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include 10 | 11 | LOCAL_SRC_FILES := blip_buf.c unifontfull.cpp soundchip.cpp hlesoundchip.cpp graphics.cpp ssinter.cpp fextra.cpp main.cpp 12 | 13 | LOCAL_SHARED_LIBRARIES := SDL2 14 | 15 | LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog 16 | 17 | include $(BUILD_SHARED_LIBRARY) 18 | -------------------------------------------------------------------------------- /src/blip_buf.c: -------------------------------------------------------------------------------- 1 | /* blip_buf $vers. http://www.slack.net/~ant/ */ 2 | 3 | #include "blip_buf.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Library Copyright (C) 2003-2009 Shay Green. This library is free software; 11 | you can redistribute it and/or modify it under the terms of the GNU Lesser 12 | General Public License as published by the Free Software Foundation; either 13 | version 2.1 of the License, or (at your option) any later version. This 14 | library is distributed in the hope that it will be useful, but WITHOUT ANY 15 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 16 | A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 17 | details. You should have received a copy of the GNU Lesser General Public 18 | License along with this module; if not, write to the Free Software Foundation, 19 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 20 | 21 | #if defined (BLARGG_TEST) && BLARGG_TEST 22 | #include "blargg_test.h" 23 | #endif 24 | 25 | /* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000. 26 | Avoids constants that don't fit in 32 bits. */ 27 | #if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF 28 | typedef unsigned long fixed_t; 29 | enum { pre_shift = 32 }; 30 | 31 | #elif defined(ULLONG_MAX) 32 | typedef unsigned long long fixed_t; 33 | enum { pre_shift = 32 }; 34 | 35 | #else 36 | typedef unsigned fixed_t; 37 | enum { pre_shift = 0 }; 38 | 39 | #endif 40 | 41 | enum { time_bits = pre_shift + 20 }; 42 | 43 | static fixed_t const time_unit = (fixed_t) 1 << time_bits; 44 | 45 | enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */ 46 | enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */ 47 | 48 | enum { half_width = 8 }; 49 | enum { buf_extra = half_width*2 + end_frame_extra }; 50 | enum { phase_bits = 5 }; 51 | enum { phase_count = 1 << phase_bits }; 52 | enum { delta_bits = 15 }; 53 | enum { delta_unit = 1 << delta_bits }; 54 | enum { frac_bits = time_bits - pre_shift }; 55 | 56 | /* We could eliminate avail and encode whole samples in offset, but that would 57 | limit the total buffered samples to blip_max_frame. That could only be 58 | increased by decreasing time_bits, which would reduce resample ratio accuracy. 59 | */ 60 | 61 | /** Sample buffer that resamples to output rate and accumulates samples 62 | until they're read out */ 63 | struct blip_t 64 | { 65 | fixed_t factor; 66 | fixed_t offset; 67 | int avail; 68 | int size; 69 | int integrator; 70 | }; 71 | 72 | typedef int buf_t; 73 | 74 | /* probably not totally portable */ 75 | #define SAMPLES( buf ) ((buf_t*) ((buf) + 1)) 76 | 77 | /* Arithmetic (sign-preserving) right shift */ 78 | #define ARITH_SHIFT( n, shift ) \ 79 | ((n) >> (shift)) 80 | 81 | enum { max_sample = +32767 }; 82 | enum { min_sample = -32768 }; 83 | 84 | #define CLAMP( n ) \ 85 | {\ 86 | if ( (short) n != n )\ 87 | n = ARITH_SHIFT( n, 16 ) ^ max_sample;\ 88 | } 89 | 90 | static void check_assumptions( void ) 91 | { 92 | int n; 93 | 94 | #if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF 95 | #error "int must be at least 32 bits" 96 | #endif 97 | 98 | assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */ 99 | 100 | n = max_sample * 2; 101 | CLAMP( n ); 102 | assert( n == max_sample ); 103 | 104 | n = min_sample * 2; 105 | CLAMP( n ); 106 | assert( n == min_sample ); 107 | 108 | assert( blip_max_ratio <= time_unit ); 109 | assert( blip_max_frame <= (fixed_t) -1 >> time_bits ); 110 | } 111 | 112 | blip_t* blip_new( int size ) 113 | { 114 | blip_t* m; 115 | assert( size >= 0 ); 116 | 117 | m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) ); 118 | if ( m ) 119 | { 120 | m->factor = time_unit / blip_max_ratio; 121 | m->size = size; 122 | blip_clear( m ); 123 | check_assumptions(); 124 | } 125 | return m; 126 | } 127 | 128 | void blip_delete( blip_t* m ) 129 | { 130 | if ( m != NULL ) 131 | { 132 | /* Clear fields in case user tries to use after freeing */ 133 | memset( m, 0, sizeof *m ); 134 | free( m ); 135 | } 136 | } 137 | 138 | void blip_set_rates( blip_t* m, double clock_rate, double sample_rate ) 139 | { 140 | double factor = time_unit * sample_rate / clock_rate; 141 | m->factor = (fixed_t) factor; 142 | 143 | /* Fails if clock_rate exceeds maximum, relative to sample_rate */ 144 | assert( 0 <= factor - m->factor && factor - m->factor < 1 ); 145 | 146 | /* Avoid requiring math.h. Equivalent to 147 | m->factor = (int) ceil( factor ) */ 148 | if ( m->factor < factor ) 149 | m->factor++; 150 | 151 | /* At this point, factor is most likely rounded up, but could still 152 | have been rounded down in the floating-point calculation. */ 153 | } 154 | 155 | void blip_clear( blip_t* m ) 156 | { 157 | /* We could set offset to 0, factor/2, or factor-1. 0 is suitable if 158 | factor is rounded up. factor-1 is suitable if factor is rounded down. 159 | Since we don't know rounding direction, factor/2 accommodates either, 160 | with the slight loss of showing an error in half the time. Since for 161 | a 64-bit factor this is years, the halving isn't a problem. */ 162 | 163 | m->offset = m->factor / 2; 164 | m->avail = 0; 165 | m->integrator = 0; 166 | memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) ); 167 | } 168 | 169 | int blip_clocks_needed( const blip_t* m, int samples ) 170 | { 171 | fixed_t needed; 172 | 173 | /* Fails if buffer can't hold that many more samples */ 174 | assert( samples >= 0 && m->avail + samples <= m->size ); 175 | 176 | needed = (fixed_t) samples * time_unit; 177 | if ( needed < m->offset ) 178 | return 0; 179 | 180 | return (needed - m->offset + m->factor - 1) / m->factor; 181 | } 182 | 183 | void blip_end_frame( blip_t* m, unsigned t ) 184 | { 185 | fixed_t off = t * m->factor + m->offset; 186 | m->avail += off >> time_bits; 187 | m->offset = off & (time_unit - 1); 188 | 189 | /* Fails if buffer size was exceeded */ 190 | assert( m->avail <= m->size ); 191 | } 192 | 193 | int blip_samples_avail( const blip_t* m ) 194 | { 195 | return m->avail; 196 | } 197 | 198 | static void remove_samples( blip_t* m, int count ) 199 | { 200 | buf_t* buf = SAMPLES( m ); 201 | int remain = m->avail + buf_extra - count; 202 | m->avail -= count; 203 | 204 | memmove( &buf [0], &buf [count], remain * sizeof buf [0] ); 205 | memset( &buf [remain], 0, count * sizeof buf [0] ); 206 | } 207 | 208 | int blip_read_samples( blip_t* m, short out [], int count, int stereo ) 209 | { 210 | assert( count >= 0 ); 211 | 212 | if ( count > m->avail ) 213 | count = m->avail; 214 | 215 | if ( count ) 216 | { 217 | int const step = stereo ? 2 : 1; 218 | buf_t const* in = SAMPLES( m ); 219 | buf_t const* end = in + count; 220 | int sum = m->integrator; 221 | do 222 | { 223 | /* Eliminate fraction */ 224 | int s = ARITH_SHIFT( sum, delta_bits ); 225 | 226 | sum += *in++; 227 | 228 | CLAMP( s ); 229 | 230 | *out = s; 231 | out += step; 232 | 233 | /* High-pass filter */ 234 | sum -= s << (delta_bits - bass_shift); 235 | } 236 | while ( in != end ); 237 | m->integrator = sum; 238 | 239 | remove_samples( m, count ); 240 | } 241 | 242 | return count; 243 | } 244 | 245 | /* Things that didn't help performance on x86: 246 | __attribute__((aligned(128))) 247 | #define short int 248 | restrict 249 | */ 250 | 251 | /* Sinc_Generator( 0.9, 0.55, 4.5 ) */ 252 | static short const bl_step [phase_count + 1] [half_width] = 253 | { 254 | { 43, -115, 350, -488, 1136, -914, 5861,21022}, 255 | { 44, -118, 348, -473, 1076, -799, 5274,21001}, 256 | { 45, -121, 344, -454, 1011, -677, 4706,20936}, 257 | { 46, -122, 336, -431, 942, -549, 4156,20829}, 258 | { 47, -123, 327, -404, 868, -418, 3629,20679}, 259 | { 47, -122, 316, -375, 792, -285, 3124,20488}, 260 | { 47, -120, 303, -344, 714, -151, 2644,20256}, 261 | { 46, -117, 289, -310, 634, -17, 2188,19985}, 262 | { 46, -114, 273, -275, 553, 117, 1758,19675}, 263 | { 44, -108, 255, -237, 471, 247, 1356,19327}, 264 | { 43, -103, 237, -199, 390, 373, 981,18944}, 265 | { 42, -98, 218, -160, 310, 495, 633,18527}, 266 | { 40, -91, 198, -121, 231, 611, 314,18078}, 267 | { 38, -84, 178, -81, 153, 722, 22,17599}, 268 | { 36, -76, 157, -43, 80, 824, -241,17092}, 269 | { 34, -68, 135, -3, 8, 919, -476,16558}, 270 | { 32, -61, 115, 34, -60, 1006, -683,16001}, 271 | { 29, -52, 94, 70, -123, 1083, -862,15422}, 272 | { 27, -44, 73, 106, -184, 1152,-1015,14824}, 273 | { 25, -36, 53, 139, -239, 1211,-1142,14210}, 274 | { 22, -27, 34, 170, -290, 1261,-1244,13582}, 275 | { 20, -20, 16, 199, -335, 1301,-1322,12942}, 276 | { 18, -12, -3, 226, -375, 1331,-1376,12293}, 277 | { 15, -4, -19, 250, -410, 1351,-1408,11638}, 278 | { 13, 3, -35, 272, -439, 1361,-1419,10979}, 279 | { 11, 9, -49, 292, -464, 1362,-1410,10319}, 280 | { 9, 16, -63, 309, -483, 1354,-1383, 9660}, 281 | { 7, 22, -75, 322, -496, 1337,-1339, 9005}, 282 | { 6, 26, -85, 333, -504, 1312,-1280, 8355}, 283 | { 4, 31, -94, 341, -507, 1278,-1205, 7713}, 284 | { 3, 35, -102, 347, -506, 1238,-1119, 7082}, 285 | { 1, 40, -110, 350, -499, 1190,-1021, 6464}, 286 | { 0, 43, -115, 350, -488, 1136, -914, 5861} 287 | }; 288 | 289 | /* Shifting by pre_shift allows calculation using unsigned int rather than 290 | possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient. 291 | And by having pre_shift 32, a 32-bit platform can easily do the shift by 292 | simply ignoring the low half. */ 293 | 294 | void blip_add_delta( blip_t* m, unsigned time, int delta ) 295 | { 296 | unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); 297 | buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); 298 | 299 | int const phase_shift = frac_bits - phase_bits; 300 | int phase = fixed >> phase_shift & (phase_count - 1); 301 | short const* in = bl_step [phase]; 302 | short const* rev = bl_step [phase_count - phase]; 303 | 304 | int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1); 305 | int delta2 = (delta * interp) >> delta_bits; 306 | delta -= delta2; 307 | 308 | /* Fails if buffer size was exceeded */ 309 | assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); 310 | 311 | out [0] += in[0]*delta + in[half_width+0]*delta2; 312 | out [1] += in[1]*delta + in[half_width+1]*delta2; 313 | out [2] += in[2]*delta + in[half_width+2]*delta2; 314 | out [3] += in[3]*delta + in[half_width+3]*delta2; 315 | out [4] += in[4]*delta + in[half_width+4]*delta2; 316 | out [5] += in[5]*delta + in[half_width+5]*delta2; 317 | out [6] += in[6]*delta + in[half_width+6]*delta2; 318 | out [7] += in[7]*delta + in[half_width+7]*delta2; 319 | 320 | in = rev; 321 | out [ 8] += in[7]*delta + in[7-half_width]*delta2; 322 | out [ 9] += in[6]*delta + in[6-half_width]*delta2; 323 | out [10] += in[5]*delta + in[5-half_width]*delta2; 324 | out [11] += in[4]*delta + in[4-half_width]*delta2; 325 | out [12] += in[3]*delta + in[3-half_width]*delta2; 326 | out [13] += in[2]*delta + in[2-half_width]*delta2; 327 | out [14] += in[1]*delta + in[1-half_width]*delta2; 328 | out [15] += in[0]*delta + in[0-half_width]*delta2; 329 | } 330 | 331 | void blip_add_delta_fast( blip_t* m, unsigned time, int delta ) 332 | { 333 | unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); 334 | buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); 335 | 336 | int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1); 337 | int delta2 = delta * interp; 338 | 339 | /* Fails if buffer size was exceeded */ 340 | assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); 341 | 342 | out [7] += delta * delta_unit - delta2; 343 | out [8] += delta2; 344 | } 345 | -------------------------------------------------------------------------------- /src/blip_buf.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | Sample buffer that resamples from input clock rate to output sample rate */ 3 | 4 | /* blip_buf $vers */ 5 | #ifndef BLIP_BUF_H 6 | #define BLIP_BUF_H 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /** First parameter of most functions is blip_t*, or const blip_t* if nothing 13 | is changed. */ 14 | typedef struct blip_t blip_t; 15 | 16 | /** Creates new buffer that can hold at most sample_count samples. Sets rates 17 | so that there are blip_max_ratio clocks per sample. Returns pointer to new 18 | buffer, or NULL if insufficient memory. */ 19 | blip_t* blip_new( int sample_count ); 20 | 21 | /** Sets approximate input clock rate and output sample rate. For every 22 | clock_rate input clocks, approximately sample_rate samples are generated. */ 23 | void blip_set_rates( blip_t*, double clock_rate, double sample_rate ); 24 | 25 | enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate, 26 | clock_rate must not be greater than sample_rate*blip_max_ratio. */ 27 | blip_max_ratio = 1 << 20 }; 28 | 29 | /** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */ 30 | void blip_clear( blip_t* ); 31 | 32 | /** Adds positive/negative delta into buffer at specified clock time. */ 33 | void blip_add_delta( blip_t*, unsigned int clock_time, int delta ); 34 | 35 | /** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */ 36 | void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta ); 37 | 38 | /** Length of time frame, in clocks, needed to make sample_count additional 39 | samples available. */ 40 | int blip_clocks_needed( const blip_t*, int sample_count ); 41 | 42 | enum { /** Maximum number of samples that can be generated from one time frame. */ 43 | blip_max_frame = 4000 }; 44 | 45 | /** Makes input clocks before clock_duration available for reading as output 46 | samples. Also begins new time frame at clock_duration, so that clock time 0 in 47 | the new time frame specifies the same clock as clock_duration in the old time 48 | frame specified. Deltas can have been added slightly past clock_duration (up to 49 | however many clocks there are in two output samples). */ 50 | void blip_end_frame( blip_t*, unsigned int clock_duration ); 51 | 52 | /** Number of buffered samples available for reading. */ 53 | int blip_samples_avail( const blip_t* ); 54 | 55 | /** Reads and removes at most 'count' samples and writes them to 'out'. If 56 | 'stereo' is true, writes output to every other element of 'out', allowing easy 57 | interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed 58 | samples. Returns number of samples actually read. */ 59 | int blip_read_samples( blip_t*, short out [], int count, int stereo ); 60 | 61 | /** Frees buffer. No effect if NULL is passed. */ 62 | void blip_delete( blip_t* ); 63 | 64 | 65 | /* Deprecated */ 66 | typedef blip_t blip_buffer_t; 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/blip_buf.txt: -------------------------------------------------------------------------------- 1 | blip_buf $vers 2 | -------------- 3 | Author : Shay Green 4 | Website : http://www.slack.net/~ant/ 5 | License : GNU Lesser General Public License (LGPL) 6 | 7 | 8 | Contents 9 | -------- 10 | * Overview 11 | * Buffer creation 12 | * Waveform generation 13 | * Time frames 14 | * Complex waveforms 15 | * Sample buffering 16 | * Thanks 17 | 18 | 19 | Overview 20 | -------- 21 | This library resamples audio waveforms from input clock rate to output 22 | sample rate. Usage follows this general pattern: 23 | 24 | * Create buffer with blip_new(). 25 | * Set clock rate and sample rate with blip_set_rates(). 26 | * Waveform generation loop: 27 | - Generate several clocks of waveform with blip_add_delta(). 28 | - End time frame with blip_end_frame(). 29 | - Read samples from buffer with blip_read_samples(). 30 | * Free buffer with blip_delete(). 31 | 32 | 33 | Buffer creation 34 | --------------- 35 | Before synthesis, a buffer must be created with blip_new(). Its size is 36 | the maximum number of unread samples it can hold. For most uses, this 37 | can be 1/10 the sample rate or less, since samples will usually be read 38 | out immediately after being generated. 39 | 40 | After the buffer is created, the input clock rate and output sample rate 41 | must be set with blip_set_rates(). This determines how many input clocks 42 | there are per second, and how many output samples are generated per 43 | second. 44 | 45 | If the compiler supports a 64-bit integer type, then the input-output 46 | ratio is stored very accurately. If the compiler only supports a 32-bit 47 | integer type, then the ratio is stored with only 20 fraction bits, so 48 | some ratios cannot be represented exactly (for example, sample 49 | rate=48000 and clock rate=48001). The ratio is internally rounded up, so 50 | there will never be fewer than 'sample rate' samples per second. Having 51 | too many per second is generally better than having too few. 52 | 53 | 54 | Waveform generation 55 | ------------------- 56 | Waveforms are generated at the input clock rate. Consider a simple 57 | square wave with 8 clocks per cycle (4 clocks high, 4 clocks low): 58 | 59 | |<-- 8 clocks ->| 60 | +5| ._._._._ ._._._._ ._._._._ ._._ 61 | | | | | | | | | 62 | Amp 0|._._._._ | | | | | | 63 | | | | | | | | 64 | -5| ._._._._ ._._._._ ._._._._ 65 | * . . . * . . . * . . . * . . . * . . . * . . . * . . . * . 66 | Time 0 4 8 12 16 20 24 28 67 | 68 | The wave changes amplitude at time points 0, 4, 8, 12, 16, etc. 69 | 70 | The following generates the amplitude at every clock of above waveform 71 | at the input clock rate: 72 | 73 | int wave [30]; 74 | 75 | for ( int i = 4; i < 30; ++i ) 76 | { 77 | if ( i % 8 < 4 ) 78 | wave [i] = -5; 79 | else 80 | wave [i] = +5; 81 | } 82 | 83 | Without this library, the wave array would then need to be resampled 84 | from the input clock rate to the output sample rate. This library does 85 | this resampling internally, so it won't be discussed further; waveform 86 | generation code can focus entirely on the input clocks. 87 | 88 | Rather than specify the amplitude at every clock, this library merely 89 | needs to know the points where the amplitude CHANGES, referred to as a 90 | delta. The time of a delta is specified with a clock count. The deltas 91 | for this square wave are shown below the time points they occur at: 92 | 93 | +5| ._._._._ ._._._._ ._._._._ ._._ 94 | | | | | | | | | 95 | Amp 0|._._._._ | | | | | | 96 | | | | | | | | 97 | -5| ._._._._ ._._._._ ._._._._ 98 | * . . . * . . . * . . . * . . . * . . . * . . . * . . . * . 99 | Time 0 4 8 12 16 20 24 28 100 | Delta +5 -10 +10 -10 +10 -10 +10 101 | 102 | The following calls generate the above waveform: 103 | 104 | blip_add_delta( blip, 4, +5 ); 105 | blip_add_delta( blip, 8, -10 ); 106 | blip_add_delta( blip, 12, +10 ); 107 | blip_add_delta( blip, 16, -10 ); 108 | blip_add_delta( blip, 20, +10 ); 109 | blip_add_delta( blip, 24, -10 ); 110 | blip_add_delta( blip, 28, +10 ); 111 | 112 | In the examples above, the amplitudes are small for clarity. The 16-bit 113 | sample range is -32768 to +32767, so actual waveform amplitudes would 114 | need to be in the thousands to be audible (for example, -5000 to +5000). 115 | 116 | This library allows waveform generation code to pay NO attention to the 117 | output sample rate. It can focus ENTIRELY on the essence of the 118 | waveform: the points where its amplitude changes. Since these points can 119 | be efficiently generated in a loop, synthesis is efficient. Sound chip 120 | emulation code can be structured to allow full accuracy down to a single 121 | clock, with the emulated CPU being able to simply tell the sound chip to 122 | "emulate from wherever you left off, up to clock time T within the 123 | current time frame". 124 | 125 | 126 | Time frames 127 | ----------- 128 | Since time keeps increasing, if left unchecked, at some point it would 129 | overflow the range of an integer. This library's solution to the problem 130 | is to break waveform generation into time frames of moderate length. 131 | Clock counts within a time frame are thus relative to the beginning of 132 | the frame, where 0 is the beginning of the frame. When a time frame of 133 | length T is ended, what was at time T in the old time frame is now at 134 | time 0 in the new time frame. Breaking the above waveform into time 135 | frames of 10 clocks each looks like this: 136 | 137 | +5| ._._._._ ._._._._ ._._._._ ._._ 138 | | | | | | | | | 139 | Amp 0|._._._._ | | | | | | 140 | | | | | | | | 141 | -5| ._._._._ ._._._._ ._._._._ 142 | * . . . * . . . * . . . * . . . * . . . * . . . * . . . * . 143 | Time |0 4 8 | 2 6 |0 4 8 | 144 | | first time frame | second time frame | third time frame | 145 | |<--- 10 clocks --->|<--- 10 clocks --->|<--- 10 clocks --->| 146 | 147 | The following calls generate the above waveform. After they execute, the 148 | first 30 clocks of the waveform will have been resampled and be 149 | available as output samples for reading with blip_read_samples(). 150 | 151 | blip_add_delta( blip, 4, +5 ); 152 | blip_add_delta( blip, 8, -10 ); 153 | blip_end_frame( blip, 10 ); 154 | 155 | blip_add_delta( blip, 2, +10 ); 156 | blip_add_delta( blip, 6, -10 ); 157 | blip_end_frame( blip, 10 ); 158 | 159 | blip_add_delta( blip, 0, +10 ); 160 | blip_add_delta( blip, 4, -10 ); 161 | blip_add_delta( blip, 8, +10 ); 162 | blip_end_frame( blip, 10 ); 163 | ... 164 | 165 | Time frames can be a convenient length, and the length can vary from one 166 | frame to the next. Once a time frame is ended, the resulting output 167 | samples become available for reading immediately, and no more deltas can 168 | be added to it. 169 | 170 | There is a limit of about 4000 output samples per time frame. The number 171 | of clocks depends on the clock rate. At common sample rates, this allows 172 | time frames of at least 1/15 second, plenty for most uses. This limit 173 | allows increased resampling ratio accuracy. 174 | 175 | In an emulator, it is usually convenient to have audio time frames 176 | correspond to video frames, where the CPU's clock counter is reset at 177 | the beginning of each video frame and thus can be used directly as the 178 | relative clock counts for audio time frames. 179 | 180 | 181 | Complex waveforms 182 | ----------------- 183 | Any sort of waveform can be generated, not just a square wave. For 184 | example, a saw-like wave: 185 | 186 | +5| ._._._._ ._._._._ ._._ 187 | | | | | | | 188 | Amp 0|._._._._ | ._._._._ | ._._._._ 189 | | | | | | 190 | -5| ._._._._ ._._._._ 191 | * . . . * . . . * . . . * . . . * . . . * . . . * . . . * . 192 | Time 0 4 8 12 16 20 24 28 193 | Delta +5 -10 +5 +5 -10 +5 +5 194 | 195 | Code to generate above waveform: 196 | 197 | blip_add_delta( blip, 4, +5 ); 198 | blip_add_delta( blip, 8, -10 ); 199 | blip_add_delta( blip, 12, +5 ); 200 | blip_add_delta( blip, 16, +5 ); 201 | blip_add_delta( blip, 20, +10 ); 202 | blip_add_delta( blip, 24, +5 ); 203 | blip_add_delta( blip, 28, +5 ); 204 | 205 | Similarly, multiple waveforms can be added within a time frame without 206 | problem. It doesn't matter what order they're added, because all the 207 | library needs are the deltas. The synthesis code doesn't need to know 208 | all the waveforms at once either; it can calculate and add the deltas 209 | for each waveform individually. Deltas don't need to be added in 210 | chronological order either. 211 | 212 | 213 | Sample buffering 214 | ---------------- 215 | Sample buffering is very flexible. Once a time frame is ended, the 216 | resampled waveforms become output samples that are immediately made 217 | available for reading with blip_read_samples(). They don't have to be 218 | read immediately; they can be allowed to accumulate in the buffer, with 219 | each time frame appending more samples to the buffer. When reading, some 220 | or all of the samples in can be read out, with the remaining unread 221 | samples staying in the buffer for later. Usually a program will 222 | immediately read all available samples after ending a time frame and 223 | play them immediately. In some systems, a program needs samples in 224 | fixed-length blocks; in that case, it would keep generating time frames 225 | until some number of samples are available, then read only that many, 226 | even if slightly more were available in the buffer. 227 | 228 | In some systems, one wants to run waveform generation for exactly the 229 | number of clocks necessary to generate some desired number of output 230 | samples, and no more. In that case, use blip_clocks_needed( blip, N ) to 231 | find out how many clocks are needed to generate N additional samples. 232 | Ending a time frame with this value will result in exactly N more 233 | samples becoming available for reading. 234 | 235 | 236 | Thanks 237 | ------ 238 | Thanks to Jsr (FamiTracker author), the Mednafen team (multi-system 239 | emulator), ShizZie (Nhes GMB author), Marcel van Tongeren, Luke Molnar 240 | (UberNES author), Fredrick Meunier (Fuse contributor) for using and 241 | giving feedback for another similar library. Thanks to Disch for his 242 | interest and discussions about the synthesis algorithm itself, and for 243 | writing his own implementation of it (Schpune) rather than just using 244 | mine. Thanks to Xodnizel for Festalon, whose sound quality got me 245 | interested in video game sound emulation in the first place, and where I 246 | first came up with the algorithm while optimizing its brute-force 247 | filter. 248 | 249 | -- 250 | Shay Green 251 | -------------------------------------------------------------------------------- /src/fextra.cpp: -------------------------------------------------------------------------------- 1 | #include "fextra.h" 2 | 3 | size_t fsize(FILE* f) { 4 | size_t tell, ret; 5 | tell=ftell(f); 6 | fseek(f,0,SEEK_END); 7 | ret=ftell(f); 8 | fseek(f,tell,SEEK_SET); 9 | return ret; 10 | } 11 | 12 | short fgetsh(FILE* f) { 13 | short ret; 14 | fread(&ret,sizeof(short),1,f); 15 | return ret; 16 | } 17 | 18 | int fgeti(FILE* f) { 19 | int ret; 20 | fread(&ret,sizeof(int),1,f); 21 | return ret; 22 | } 23 | -------------------------------------------------------------------------------- /src/fextra.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | size_t fsize(FILE* f); 4 | 5 | // READ FUNCTIONS // 6 | 7 | // reading 8 | short fgetsh(FILE* f); 9 | int fgeti(FILE* f); 10 | long long fgetll(FILE* f); 11 | 12 | // reading float/double 13 | float fgetf(FILE* f); 14 | double fgetd(FILE* f); 15 | 16 | // WRITE FUNCTIONS // 17 | 18 | // writing 19 | int fputsh(short c, FILE* f); 20 | int fputi(int c, FILE* f); 21 | int fputll(long long c, FILE* f); 22 | 23 | // writing float/double 24 | int fputf(float c, FILE* f); 25 | int fputd(double c, FILE* f); 26 | -------------------------------------------------------------------------------- /src/fonts.h: -------------------------------------------------------------------------------- 1 | #ifndef _FONTS_H 2 | #define _FONTS_H 3 | extern const unsigned int defFont_main_compressed_size; 4 | extern const unsigned int defFont_main_compressed_data[]; 5 | extern const unsigned int defFont_pat_compressed_size; 6 | extern const unsigned int defFont_pat_compressed_data[]; 7 | #endif -------------------------------------------------------------------------------- /src/hlesoundchip.cpp: -------------------------------------------------------------------------------- 1 | #include "soundchip.h" 2 | #include 3 | 4 | #ifndef M_PI 5 | #define M_PI 3.141592653589793238 6 | #endif 7 | 8 | void HLESoundchip::NextSample(short* l, short* r) { 9 | *l=0; 10 | *r=0; 11 | } 12 | 13 | void HLESoundchip::Init() { 14 | Reset(); 15 | for (int i=0; i<65280; i++) { 16 | pcm[i]=0; 17 | } 18 | } 19 | 20 | void HLESoundchip::Reset() { 21 | for (int i=0; i<8; i++) { 22 | } 23 | memset(chan,0,sizeof(channel)*8); 24 | } 25 | -------------------------------------------------------------------------------- /src/macroStatus.cpp: -------------------------------------------------------------------------------- 1 | #include "tracker.h" 2 | 3 | void MacroStatus::next() { 4 | hasChanged=false; 5 | if (macro==NULL) { 6 | pos=0; 7 | return; 8 | } 9 | if (pos>=(int)macro->cmds.size()) return; 10 | if (--waitTime>0) return; 11 | 12 | bool getOut=false; 13 | int iterations=0; 14 | while (!getOut) { 15 | pos++; 16 | if (pos>=(int)macro->cmds.size()) break; 17 | MacroCommand cmd=macro->cmds[pos]; 18 | switch (cmd.type&127) { 19 | case cmdEnd: 20 | pos=macro->cmds.size(); 21 | break; 22 | case cmdSet: 23 | hasChanged=true; 24 | value=cmd.value; 25 | break; 26 | case cmdWait: 27 | waitTime=cmd.value; 28 | getOut=true; 29 | break; 30 | case cmdWaitRel: 31 | if (!released) { 32 | getOut=true; 33 | pos--; 34 | } 35 | break; 36 | case cmdLoop: 37 | pos=cmd.value-1; 38 | break; 39 | case cmdLoopRel: 40 | if (!released) pos=cmd.value-1; 41 | break; 42 | case cmdAdd: 43 | hasChanged=true; 44 | value+=cmd.value; 45 | break; 46 | case cmdSub: 47 | hasChanged=true; 48 | if (value16) break; 58 | } 59 | } 60 | 61 | void MacroStatus::release() { 62 | released=true; 63 | 64 | if (macro!=NULL) { 65 | if (macro->jumpRelease>-1) { 66 | pos=macro->jumpRelease-1; 67 | } 68 | } 69 | } 70 | 71 | void MacroStatus::load(Macro* m) { 72 | macro=m; 73 | pos=-1; 74 | waitTime=0; 75 | released=false; 76 | hasChanged=false; 77 | value=0; 78 | } 79 | 80 | MacroStatus::MacroStatus() { 81 | load(NULL); 82 | } -------------------------------------------------------------------------------- /src/nsstub.h: -------------------------------------------------------------------------------- 1 | double nsStubDPI(); 2 | char* nsStubFontPath(const char* name); 3 | -------------------------------------------------------------------------------- /src/nsstub.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include "nsstub.h" 3 | 4 | // this may need to be replaced 5 | double nsStubDPI() { 6 | CGFloat val; 7 | val=[[NSScreen mainScreen] backingScaleFactor]; 8 | return (double)val; 9 | } 10 | 11 | char* nsStubFontPath(const char* name) { 12 | return ""; 13 | }; 14 | -------------------------------------------------------------------------------- /src/song.cpp: -------------------------------------------------------------------------------- 1 | #include "tracker.h" 2 | 3 | static Pattern emptyPat; 4 | 5 | Pattern* Song::getPattern(unsigned char num, bool create) { 6 | if (pat[num]==NULL) { 7 | if (!create) return &emptyPat; 8 | //printf("\x1b[1;32mCreating pattern %d.\x1b[m\n",num); 9 | pat[num]=new Pattern; 10 | } 11 | return pat[num]; 12 | } 13 | 14 | Song::Song(): 15 | version(TRACKER_VER), 16 | insC(0), 17 | patC(0), 18 | orders(0), 19 | speed(6), 20 | flags(0), 21 | tempo(0), 22 | DFM(0), 23 | channels(8), 24 | macrosC(0), 25 | globalVol(128), 26 | globalPan(128), 27 | detune(0), 28 | len(0) { 29 | memcpy(header,"TRACK8BT",8); 30 | memset(name,0,32); 31 | pcmPtr[0]=0; pcmPtr[1]=0; 32 | commentPtr[0]=0; commentPtr[1]=0; 33 | for (int i=0; i<32; i++) { 34 | defaultVol[i]=128; 35 | defaultPan[i]=((i+1)&2)?96:-96; 36 | } 37 | memset(order,0,256); 38 | for (int i=0; i<256; i++) { 39 | ins[i]=new Instrument; 40 | pat[i]=NULL; 41 | } 42 | } 43 | 44 | Song::~Song() { 45 | for (int i=0; i<256; i++) { 46 | if (ins[i]!=NULL) { 47 | delete ins[i]; 48 | ins[i]=NULL; 49 | } 50 | if (pat[i]!=NULL) { 51 | //printf("\x1b[1;31mDeleting pattern %d.\x1b[m\n",i); 52 | delete pat[i]; 53 | pat[i]=NULL; 54 | } 55 | } 56 | for (Macro* i: macros) { 57 | delete i; 58 | } 59 | macros.clear(); 60 | } 61 | -------------------------------------------------------------------------------- /src/soundchip.cpp: -------------------------------------------------------------------------------- 1 | #include "soundchip.h" 2 | #include 3 | 4 | #ifndef M_PI 5 | #define M_PI 3.141592653589793238 6 | #endif 7 | 8 | #define minval(a,b) (((a)<(b))?(a):(b)) 9 | #define maxval(a,b) (((a)>(b))?(a):(b)) 10 | 11 | void soundchip::NextSample(short* l, short* r) { 12 | for (int i=0; i<8; i++) { 13 | if (chan[i].vol==0 && !chan[i].flags.swvol) {fns[i]=0; continue;} 14 | if (chan[i].flags.pcm) { 15 | ns[i]=pcm[chan[i].pcmpos]; 16 | } else switch (chan[i].flags.shape) { 17 | case 0: 18 | ns[i]=(((cycle[i]>>15)&127)>chan[i].duty)*127; 19 | break; 20 | case 1: 21 | ns[i]=cycle[i]>>14; 22 | break; 23 | case 2: 24 | ns[i]=SCsine[(cycle[i]>>14)&255]; 25 | break; 26 | case 3: 27 | ns[i]=SCtriangle[(cycle[i]>>14)&255]; 28 | break; 29 | case 4: case 5: 30 | ns[i]=(lfsr[i]&1)*127; 31 | break; 32 | case 6: 33 | ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCsine[(cycle[i]>>14)&255]; 34 | break; 35 | case 7: 36 | ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCtriangle[(cycle[i]>>14)&255]; 37 | break; 38 | } 39 | 40 | if (chan[i].flags.pcm) { 41 | if (chan[i].freq>0x8000) { 42 | pcmdec[i]+=0x8000; 43 | } else { 44 | pcmdec[i]+=chan[i].freq; 45 | } 46 | if (pcmdec[i]>=32768) { 47 | pcmdec[i]-=32768; 48 | if (chan[i].pcmpos>4)&3) { 65 | case 0: 66 | cycle[i]+=chan[i].freq*1-(chan[i].freq>>3); 67 | break; 68 | case 1: 69 | cycle[i]+=chan[i].freq*2-(chan[i].freq>>3); 70 | break; 71 | case 2: 72 | cycle[i]+=chan[i].freq*4-(chan[i].freq>>3); 73 | break; 74 | case 3: 75 | cycle[i]+=chan[i].freq*8-(chan[i].freq>>3); 76 | break; 77 | } 78 | } else { 79 | cycle[i]+=chan[i].freq; 80 | } 81 | if ((cycle[i]&0xf80000)!=(ocycle[i]&0xf80000)) { 82 | if (chan[i].flags.shape==4) { 83 | lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<31); 84 | } else { 85 | switch ((chan[i].duty>>4)&3) { 86 | case 0: 87 | lfsr[i]=(lfsr[i]>>1|(((lfsr[i] >> 3) ^ (lfsr[i] >> 4) ) & 1)<<5); 88 | break; 89 | case 1: 90 | lfsr[i]=(lfsr[i]>>1|(((lfsr[i] >> 2) ^ (lfsr[i] >> 3) ) & 1)<<5); 91 | break; 92 | case 2: 93 | lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ) & 1)<<5); 94 | break; 95 | case 3: 96 | lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<5); 97 | break; 98 | } 99 | if ((lfsr[i]&63)==0) { 100 | lfsr[i]=0xaaaa; 101 | } 102 | } 103 | } 104 | if (chan[i].flags.restim) { 105 | if (--rcycle[i]<=0) { 106 | cycle[i]=0; 107 | rcycle[i]=chan[i].restimer; 108 | lfsr[i]=0xaaaa; 109 | } 110 | } 111 | } 112 | //ns[i]=(char)((short)ns[i]*(short)vol[i]/256); 113 | fns[i]=ns[i]*chan[i].vol*2; 114 | if (chan[i].flags.fmode!=0) { 115 | int ff=chan[i].cutoff; 116 | nslow[i]=nslow[i]+(((ff)*nsband[i])>>16); 117 | nshigh[i]=fns[i]-nslow[i]-(((256-chan[i].reson)*nsband[i])>>8); 118 | nsband[i]=(((ff)*nshigh[i])>>16)+nsband[i]; 119 | fns[i]=(((chan[i].flags.fmode&1)?(nslow[i]):(0))+((chan[i].flags.fmode&2)?(nshigh[i]):(0))+((chan[i].flags.fmode&4)?(nsband[i]):(0))); 120 | } 121 | nsL[i]=(fns[i]*SCpantabL[(unsigned char)chan[i].pan])>>8; 122 | nsR[i]=(fns[i]*SCpantabR[(unsigned char)chan[i].pan])>>8; 123 | oldfreq[i]=chan[i].freq; 124 | oldflags[i]=chan[i].flags.flags; 125 | if (chan[i].flags.swvol) { 126 | if (--swvolt[i]<=0) { 127 | swvolt[i]=chan[i].swvol.speed; 128 | if (chan[i].swvol.dir) { 129 | chan[i].vol+=chan[i].swvol.amt; 130 | if (chan[i].vol>chan[i].swvol.bound && !chan[i].swvol.loop) { 131 | chan[i].vol=chan[i].swvol.bound; 132 | } 133 | if (chan[i].vol&0x80) { 134 | if (chan[i].swvol.loop) { 135 | if (chan[i].swvol.loopi) { 136 | chan[i].swvol.dir=!chan[i].swvol.dir; 137 | chan[i].vol=0xff-chan[i].vol; 138 | } else { 139 | chan[i].vol&=~0x80; 140 | } 141 | } else { 142 | chan[i].vol=0x7f; 143 | } 144 | } 145 | } else { 146 | chan[i].vol-=chan[i].swvol.amt; 147 | if (chan[i].vol&0x80) { 148 | if (chan[i].swvol.loop) { 149 | if (chan[i].swvol.loopi) { 150 | chan[i].swvol.dir=!chan[i].swvol.dir; 151 | chan[i].vol=-chan[i].vol; 152 | } else { 153 | chan[i].vol&=~0x80; 154 | } 155 | } else { 156 | chan[i].vol=0x0; 157 | } 158 | } 159 | if (chan[i].vol(0xffff-chan[i].swfreq.amt)) { 170 | chan[i].freq=0xffff; 171 | } else { 172 | chan[i].freq=(chan[i].freq*(0x80+chan[i].swfreq.amt))>>7; 173 | if ((chan[i].freq>>8)>chan[i].swfreq.bound) { 174 | chan[i].freq=chan[i].swfreq.bound<<8; 175 | } 176 | } 177 | } else { 178 | if (chan[i].freq>8; 182 | if ((chan[i].freq>>8)(0xffff-chan[i].swcut.amt)) { 194 | chan[i].cutoff=0xffff; 195 | } else { 196 | chan[i].cutoff+=chan[i].swcut.amt; 197 | if ((chan[i].cutoff>>8)>chan[i].swcut.bound) { 198 | chan[i].cutoff=chan[i].swcut.bound<<8; 199 | } 200 | } 201 | } else { 202 | if (chan[i].cutoff>11; 206 | if ((chan[i].cutoff>>8)>2;///256; 221 | tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7])>>2;///256; 222 | 223 | *l=minval(32767,maxval(-32767,tnsL));//(2047*(pnsL+tnsL-ppsL))>>11; 224 | *r=minval(32767,maxval(-32767,tnsR));//(2047*(pnsR+tnsR-ppsR))>>11; 225 | } 226 | 227 | void soundchip::Init() { 228 | Reset(); 229 | memset(pcm,0,SOUNDCHIP_PCM_SIZE); 230 | for (int i=0; i<256; i++) { 231 | SCsine[i]=sin((i/128.0f)*M_PI)*127; 232 | SCtriangle[i]=(i>127)?(255-i):(i); 233 | SCpantabL[i]=127; 234 | SCpantabR[i]=127; 235 | } 236 | for (int i=0; i<128; i++) { 237 | SCpantabL[i]=127-i; 238 | SCpantabR[128+i]=i-1; 239 | } 240 | SCpantabR[128]=0; 241 | } 242 | 243 | void soundchip::Reset() { 244 | for (int i=0; i<8; i++) { 245 | cycle[i]=0; 246 | resetfreq[i]=0; 247 | //duty[i]=64; 248 | //pan[i]=0; 249 | //cut[i]=0; 250 | //res[i]=0; 251 | //flags[i]=0; 252 | //postvol[i]=0; 253 | voldcycles[i]=0; 254 | volicycles[i]=0; 255 | fscycles[i]=0; 256 | sweep[i]=0; 257 | ns[i]=0; 258 | swvolt[i]=1; 259 | swfreqt[i]=1; 260 | swcutt[i]=1; 261 | lfsr[i]=0xaaaa; 262 | } 263 | memset(chan,0,sizeof(channel)*8); 264 | } 265 | 266 | soundchip::soundchip() { 267 | Init(); 268 | } 269 | -------------------------------------------------------------------------------- /src/soundchip.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define SOUNDCHIP_PCM_SIZE 65536 7 | 8 | class soundchip { 9 | signed char SCsine[256]; 10 | signed char SCtriangle[256]; 11 | signed char SCpantabL[256]; 12 | signed char SCpantabR[256]; 13 | unsigned int ocycle[8]; 14 | unsigned int cycle[8]; 15 | int rcycle[8]; 16 | unsigned int lfsr[8]; 17 | signed char ns[8]; 18 | int fns[8]; 19 | int nsL[8]; 20 | int nsR[8]; 21 | int nslow[8]; 22 | int nshigh[8]; 23 | int nsband[8]; 24 | int tnsL, tnsR; 25 | unsigned short oldfreq[8]; 26 | unsigned short oldflags[8]; 27 | public: 28 | unsigned short resetfreq[8]; 29 | //char pan[8]; 30 | //unsigned char res[8]; 31 | //char postvol[8]; 32 | unsigned short voldcycles[8]; 33 | unsigned short volicycles[8]; 34 | unsigned short fscycles[8]; 35 | unsigned char sweep[8]; 36 | unsigned short swvolt[8]; 37 | unsigned short swfreqt[8]; 38 | unsigned short swcutt[8]; 39 | //int pcmpos[8]; 40 | unsigned short pcmdec[8]; 41 | //int pcmend[8]; 42 | //int pcmreset[8]; 43 | //unsigned char pcmmult[8]; 44 | struct channel { 45 | unsigned short freq; 46 | signed char vol; 47 | signed char pan; 48 | union { 49 | unsigned short flags; 50 | struct { 51 | unsigned char shape: 3; 52 | unsigned char pcm: 1; 53 | unsigned char ring: 1; 54 | unsigned char fmode: 3; 55 | unsigned char resosc: 1; 56 | unsigned char resfilt: 1; 57 | unsigned char pcmloop: 1; 58 | unsigned char restim: 1; 59 | unsigned char swfreq: 1; 60 | unsigned char swvol: 1; 61 | unsigned char swcut: 1; 62 | unsigned char padding: 1; 63 | }; 64 | } flags; 65 | unsigned short cutoff; 66 | unsigned char duty; 67 | unsigned char reson; 68 | unsigned short pcmpos; 69 | unsigned short pcmbnd; 70 | unsigned short pcmrst; 71 | struct { 72 | unsigned short speed; 73 | unsigned char amt: 7; 74 | unsigned char dir: 1; 75 | unsigned char bound; 76 | } swfreq; 77 | struct { 78 | unsigned short speed; 79 | unsigned char amt: 5; 80 | unsigned char dir: 1; 81 | unsigned char loop: 1; 82 | unsigned char loopi: 1; 83 | unsigned char bound; 84 | } swvol; 85 | struct { 86 | unsigned short speed; 87 | unsigned char amt: 7; 88 | unsigned char dir: 1; 89 | unsigned char bound; 90 | } swcut; 91 | unsigned short wc; 92 | unsigned short restimer; 93 | } chan[8]; 94 | signed char pcm[SOUNDCHIP_PCM_SIZE]; 95 | void NextSample(short* l, short* r); 96 | void Init(); 97 | void Reset(); 98 | soundchip(); 99 | }; 100 | 101 | class HLESoundchip: public soundchip { 102 | public: 103 | void NextSample(short* l, short* r); 104 | void Init(); 105 | void Reset(); 106 | }; 107 | -------------------------------------------------------------------------------- /src/ssbench.cpp: -------------------------------------------------------------------------------- 1 | // specs2 soundchip sequence interpreter... benchmark! 2 | #include "soundchip.h" 3 | #include "ssinter.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef _WIN32 9 | #else 10 | #include 11 | #endif 12 | 13 | bool quit; 14 | 15 | soundchip sc; 16 | 17 | double targetSR; 18 | 19 | double noProc; 20 | 21 | double procPos; 22 | int ticks, speed; 23 | 24 | FILE* f; 25 | int frame; 26 | 27 | SSInter s; 28 | 29 | size_t fsize; 30 | 31 | float resa0[2], resa1[2]; 32 | 33 | #define resaf 0.33631372025095791864295318996109 34 | 35 | std::string str; 36 | 37 | int main(int argc, char** argv) { 38 | int which; 39 | sc.Init(); 40 | procPos=0; 41 | frame=0; 42 | ticks=0; 43 | resa0[0]=0; resa0[1]=0; 44 | resa1[0]=0; resa1[1]=0; 45 | 46 | quit=false; 47 | 48 | if (argc<2) { 49 | printf("usage: %s [-n] file\n",argv[0]); 50 | return 1; 51 | } 52 | targetSR=297500; // PAL. 53 | speed=119000; // PAL. 54 | which=1; 55 | 56 | s.init(&sc); 57 | 58 | if (strcmp(argv[1],"-n")==0) { 59 | which=2; 60 | targetSR=309000; // NTSC. 61 | speed=103103; // NTSC. 103000 for no colorburst compensation 62 | } 63 | 64 | if ((f=fopen(argv[which],"rb"))==NULL) { 65 | printf("not exist\n"); 66 | return 1; 67 | } 68 | 69 | fseek(f,0,SEEK_END); 70 | fsize=ftell(f); 71 | fseek(f,0,SEEK_SET); 72 | 73 | printf("start!\n"); 74 | 75 | for (int i=0; i<8; i++) { 76 | sc.chan[i].pan=0; 77 | sc.chan[i].duty=0x3f; 78 | } 79 | 80 | short temp[2]; 81 | int wc; 82 | int writable; 83 | while (!quit) { 84 | ticks-=20; // 20 CPU cycles per sound output cycle 85 | if (ticks<=0) { 86 | str=""; 87 | while ((wc=fgetc(f))!=EOF) { 88 | str+=wc; 89 | if (wc=='R') break; 90 | } 91 | if (feof(f)) quit=true; 92 | writable=0; 93 | s.next(str.c_str(),writable,str.size()); 94 | ticks+=speed; 95 | frame++; 96 | } 97 | sc.NextSample(&temp[0],&temp[1]); 98 | resa0[0]=resa0[0]+resaf*(temp[0]-resa0[0]); 99 | resa1[0]=resa1[0]+resaf*(resa0[0]-resa1[0]); 100 | resa1[0]=resa1[0]+resaf*(resa0[0]-resa1[0]); 101 | resa1[0]=resa1[0]+resaf*(resa0[0]-resa1[0]); 102 | 103 | resa0[1]=resa0[1]+resaf*(temp[1]-resa0[1]); 104 | resa1[1]=resa1[1]+resaf*(resa0[1]-resa1[1]); 105 | resa1[1]=resa1[1]+resaf*(resa0[1]-resa1[1]); 106 | resa1[1]=resa1[1]+resaf*(resa0[1]-resa1[1]); 107 | } 108 | 109 | printf("end!\n"); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /src/ssinter.cpp: -------------------------------------------------------------------------------- 1 | // specs2 soundchip sequence interpreter... 2 | #include "soundchip.h" 3 | #include "ssinter.h" 4 | 5 | // maximum permissible notes 6 | unsigned short noteFreqs[12]={ 7 | 0x7344, 8 | 0x7a1e, 9 | 0x8161, 10 | 0x8913, 11 | 0x913a, 12 | 0x99dc, 13 | 0xa302, 14 | 0xacb4, 15 | 0xb6f9, 16 | 0xc1da, 17 | 0xcd61, 18 | 0xd998 19 | }; 20 | 21 | int decHex(int ch) { 22 | switch (ch) { 23 | case '0': 24 | case '1': 25 | case '2': 26 | case '3': 27 | case '4': 28 | case '5': 29 | case '6': 30 | case '7': 31 | case '8': 32 | case '9': 33 | return ch-'0'; 34 | break; 35 | case 'A': 36 | case 'B': 37 | case 'C': 38 | case 'D': 39 | case 'E': 40 | case 'F': 41 | return 10+ch-'A'; 42 | break; 43 | case 'a': 44 | case 'b': 45 | case 'c': 46 | case 'd': 47 | case 'e': 48 | case 'f': 49 | return 10+ch-'a'; 50 | break; 51 | } 52 | return 0; 53 | } 54 | 55 | int bufchar(const char* buf, size_t tell, size_t bound) { 56 | if (tell>=bound) return EOF; 57 | return buf[tell]; 58 | } 59 | 60 | #define _NEXT_ bufchar(buf,set++,size) 61 | 62 | // returns false if end of stream 63 | bool SSInter::next(const char* buf, int& set, size_t size) { 64 | char temp; 65 | int c; 66 | if ((unsigned)set>=size) { 67 | return false; 68 | } 69 | while ((unsigned)setchan[curChan],0,32); 74 | out->chan[curChan].duty=0x3f; 75 | break; 76 | case '$': 77 | temp=_NEXT_; 78 | if (temp=='x') { 79 | curChan=prefChan; 80 | } else { 81 | curChan=(temp-'0')&7; 82 | } 83 | break; 84 | case 'V': 85 | out->chan[curChan].vol=(decHex(_NEXT_)<<4); 86 | out->chan[curChan].vol+=decHex(_NEXT_); 87 | break; 88 | case 'Y': 89 | out->chan[curChan].duty=(decHex(_NEXT_)<<4); 90 | out->chan[curChan].duty+=decHex(_NEXT_); 91 | break; 92 | case 'f': 93 | out->chan[curChan].freq=(decHex(_NEXT_)<<12); 94 | out->chan[curChan].freq+=(decHex(_NEXT_)<<8); 95 | out->chan[curChan].freq+=(decHex(_NEXT_)<<4); 96 | out->chan[curChan].freq+=decHex(_NEXT_); 97 | break; 98 | case 'S': 99 | out->chan[curChan].flags.shape=_NEXT_-'0'; 100 | break; 101 | case 'I': 102 | out->chan[curChan].flags.fmode=_NEXT_-'0'; 103 | break; 104 | case 'c': 105 | out->chan[curChan].cutoff=(decHex(_NEXT_)<<12); 106 | out->chan[curChan].cutoff+=(decHex(_NEXT_)<<8); 107 | out->chan[curChan].cutoff+=(decHex(_NEXT_)<<4); 108 | out->chan[curChan].cutoff+=decHex(_NEXT_); 109 | break; 110 | case 'r': 111 | out->chan[curChan].reson=(decHex(_NEXT_)<<4); 112 | out->chan[curChan].reson+=decHex(_NEXT_); 113 | break; 114 | case 'M': 115 | temp=(_NEXT_-'0')&7; 116 | out->chan[curChan].flags.swvol=!!(temp&1); 117 | out->chan[curChan].flags.swfreq=!!(temp&2); 118 | out->chan[curChan].flags.swcut=!!(temp&4); 119 | break; 120 | case 'v': 121 | out->chan[curChan].swvol.speed=(decHex(_NEXT_)<<12); 122 | out->chan[curChan].swvol.speed+=(decHex(_NEXT_)<<8); 123 | out->chan[curChan].swvol.speed+=(decHex(_NEXT_)<<4); 124 | out->chan[curChan].swvol.speed+=decHex(_NEXT_); 125 | temp=(decHex(_NEXT_)<<4); 126 | temp+=decHex(_NEXT_); 127 | out->chan[curChan].swvol.amt=temp&0x1f; 128 | out->chan[curChan].swvol.dir=!!(temp&0x20); 129 | out->chan[curChan].swvol.loop=!!(temp&0x40); 130 | out->chan[curChan].swvol.loopi=!!(temp&0x80); 131 | out->chan[curChan].swvol.bound=(decHex(_NEXT_)<<4); 132 | out->chan[curChan].swvol.bound+=decHex(_NEXT_); 133 | break; 134 | case 'k': 135 | out->chan[curChan].swfreq.speed=(decHex(_NEXT_)<<12); 136 | out->chan[curChan].swfreq.speed+=(decHex(_NEXT_)<<8); 137 | out->chan[curChan].swfreq.speed+=(decHex(_NEXT_)<<4); 138 | out->chan[curChan].swfreq.speed+=decHex(_NEXT_); 139 | temp=(decHex(_NEXT_)<<4); 140 | temp+=decHex(_NEXT_); 141 | out->chan[curChan].swfreq.amt=temp&0x7f; 142 | out->chan[curChan].swfreq.dir=!!(temp&0x80); 143 | out->chan[curChan].swfreq.bound=(decHex(_NEXT_)<<4); 144 | out->chan[curChan].swfreq.bound+=decHex(_NEXT_); 145 | break; 146 | case 'l': 147 | out->chan[curChan].swcut.speed=(decHex(_NEXT_)<<12); 148 | out->chan[curChan].swcut.speed+=(decHex(_NEXT_)<<8); 149 | out->chan[curChan].swcut.speed+=(decHex(_NEXT_)<<4); 150 | out->chan[curChan].swcut.speed+=decHex(_NEXT_); 151 | temp=(decHex(_NEXT_)<<4); 152 | temp+=decHex(_NEXT_); 153 | out->chan[curChan].swcut.amt=temp&0x7f; 154 | out->chan[curChan].swcut.dir=!!(temp&0x80); 155 | out->chan[curChan].swcut.bound=(decHex(_NEXT_)<<4); 156 | out->chan[curChan].swcut.bound+=decHex(_NEXT_); 157 | break; 158 | case 'O': 159 | octave=_NEXT_-'0'; 160 | if (octave<0) octave=0; 161 | if (octave>7) octave=7; 162 | break; 163 | case 'C': 164 | out->chan[curChan].freq=noteFreqs[0]>>(7-octave); 165 | break; 166 | case 'D': 167 | out->chan[curChan].freq=noteFreqs[2]>>(7-octave); 168 | break; 169 | case 'E': 170 | out->chan[curChan].freq=noteFreqs[4]>>(7-octave); 171 | break; 172 | case 'F': 173 | out->chan[curChan].freq=noteFreqs[5]>>(7-octave); 174 | break; 175 | case 'G': 176 | out->chan[curChan].freq=noteFreqs[7]>>(7-octave); 177 | break; 178 | case 'A': 179 | out->chan[curChan].freq=noteFreqs[9]>>(7-octave); 180 | break; 181 | case 'B': 182 | out->chan[curChan].freq=noteFreqs[11]>>(7-octave); 183 | break; 184 | case '#': 185 | c=_NEXT_; 186 | switch (c) { 187 | case 'C': 188 | out->chan[curChan].freq=noteFreqs[1]>>(7-octave); 189 | break; 190 | case 'D': 191 | out->chan[curChan].freq=noteFreqs[3]>>(7-octave); 192 | break; 193 | case 'F': 194 | out->chan[curChan].freq=noteFreqs[6]>>(7-octave); 195 | break; 196 | case 'G': 197 | out->chan[curChan].freq=noteFreqs[8]>>(7-octave); 198 | break; 199 | case 'A': 200 | out->chan[curChan].freq=noteFreqs[10]>>(7-octave); 201 | break; 202 | } 203 | break; 204 | } 205 | if (c=='R' || c==';') break; 206 | } 207 | return true; 208 | }; 209 | 210 | void SSInter::setChan(int ch) { 211 | prefChan=ch; 212 | } 213 | 214 | void SSInter::init(soundchip* where) { 215 | out=where; 216 | } 217 | -------------------------------------------------------------------------------- /src/ssinter.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class soundchip; 5 | 6 | class SSInter { 7 | soundchip* out; 8 | int octave; 9 | int curChan; 10 | int prefChan; 11 | public: 12 | bool next(const char* buf, int& set, size_t size); 13 | void setChan(int ch); 14 | void init(soundchip* where); 15 | SSInter(): out(NULL), octave(4), curChan(0), prefChan(0) {} 16 | }; 17 | -------------------------------------------------------------------------------- /src/sslisp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // ssinter tiny Lisp interpreter 4 | -------------------------------------------------------------------------------- /src/ssmain.cpp: -------------------------------------------------------------------------------- 1 | // specs2 soundchip sequence interpreter... 2 | #include "soundchip.h" 3 | #include "ssinter.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef _WIN32 9 | #include 10 | #else 11 | #include 12 | #endif 13 | 14 | #ifdef JACK 15 | #include 16 | 17 | jack_client_t* ac; 18 | jack_port_t* ao[2]; 19 | jack_status_t as; 20 | #else 21 | #ifdef HAVE_GUI 22 | #include 23 | #else 24 | #include 25 | #endif 26 | SDL_AudioDeviceID ai; 27 | SDL_AudioSpec ac; 28 | SDL_AudioSpec ar; 29 | #endif 30 | 31 | bool quit, viewMemory; 32 | 33 | int sr; 34 | 35 | soundchip sc; 36 | 37 | double targetSR; 38 | 39 | double noProc; 40 | 41 | double procPos; 42 | int ticks, speed; 43 | 44 | FILE* f; 45 | int frame; 46 | 47 | SSInter s; 48 | 49 | size_t fsize; 50 | 51 | float resa0[2], resa1[2]; 52 | 53 | #define resaf 0.33631372025095791864295318996109 54 | 55 | std::string str; 56 | 57 | #ifdef JACK 58 | int process(jack_nframes_t nframes, void* arg) { 59 | #else 60 | static void process(void* userdata, Uint8* stream, int len) { 61 | #endif 62 | float* buf[2]; 63 | short temp[2]; 64 | int wc; 65 | int writable; 66 | #ifdef JACK 67 | for (int i=0; i<2; i++) { 68 | buf[i]=(float*)jack_port_get_buffer(ao[i],nframes); 69 | } 70 | #else 71 | unsigned int nframes=len/(sizeof(float)*ar.channels); 72 | buf[0]=(float*)stream; 73 | buf[1]=&buf[0][1]; 74 | #endif 75 | 76 | for (size_t i=0; i=1) { 134 | procPos-=1; 135 | i++; 136 | } 137 | } 138 | #ifdef JACK 139 | return 0; 140 | #endif 141 | } 142 | 143 | int main(int argc, char** argv) { 144 | int which; 145 | sc.Init(); 146 | procPos=0; 147 | frame=0; 148 | ticks=0; 149 | resa0[0]=0; resa0[1]=0; 150 | resa1[0]=0; resa1[1]=0; 151 | 152 | quit=false; 153 | viewMemory=false; 154 | 155 | if (argc<2) { 156 | printf("usage: %s [-n] file\n",argv[0]); 157 | return 1; 158 | } 159 | targetSR=297500; // PAL. 160 | speed=119000; // PAL. 161 | which=1; 162 | 163 | s.init(&sc); 164 | 165 | if (strcmp(argv[1],"-n")==0) { 166 | which=2; 167 | targetSR=309000; // NTSC. 168 | speed=103103; // NTSC. 103000 for no colorburst compensation 169 | } 170 | 171 | if ((f=fopen(argv[which],"rb"))==NULL) { 172 | printf("not exist\n"); 173 | return 1; 174 | } 175 | 176 | fseek(f,0,SEEK_END); 177 | fsize=ftell(f); 178 | fseek(f,0,SEEK_SET); 179 | 180 | printf("opening audio\n"); 181 | 182 | for (int i=0; i<8; i++) { 183 | sc.chan[i].pan=0; 184 | sc.chan[i].duty=0x3f; 185 | } 186 | 187 | #ifdef JACK 188 | ac=jack_client_open("ssinter",JackNullOption,&as); 189 | if (ac==NULL) return 1; 190 | 191 | sr=jack_get_sample_rate(ac); 192 | 193 | noProc=sr/targetSR; 194 | 195 | jack_set_process_callback(ac,process,NULL); 196 | 197 | ao[0]=jack_port_register(ac,"outL",JACK_DEFAULT_AUDIO_TYPE,JackPortIsOutput,0); 198 | ao[1]=jack_port_register(ac,"outR",JACK_DEFAULT_AUDIO_TYPE,JackPortIsOutput,0); 199 | 200 | jack_activate(ac); 201 | 202 | jack_connect(ac,"ssinter:outL","system:playback_1"); 203 | jack_connect(ac,"ssinter:outR","system:playback_2"); 204 | #else 205 | SDL_Init(SDL_INIT_AUDIO); 206 | 207 | ac.freq=44100; 208 | ac.format=AUDIO_F32; 209 | ac.channels=2; 210 | ac.samples=1024; 211 | ac.callback=process; 212 | ac.userdata=NULL; 213 | printf("hmm\n"); 214 | ai=SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0,0),0,&ac,&ar,SDL_AUDIO_ALLOW_ANY_CHANGE); 215 | printf("works\n"); 216 | sr=ar.freq; 217 | noProc=sr/targetSR; 218 | 219 | SDL_PauseAudioDevice(ai,0); 220 | #endif 221 | 222 | while (!quit) { 223 | #ifdef _WIN32 224 | Sleep(50); 225 | #else 226 | usleep(50000); 227 | #endif 228 | } 229 | 230 | #ifdef JACK 231 | jack_deactivate(ac); 232 | #else 233 | SDL_CloseAudioDevice(ai); 234 | #endif 235 | return 0; 236 | } 237 | 238 | #ifdef _WIN32 239 | #include "winMain.cpp" 240 | #endif 241 | -------------------------------------------------------------------------------- /src/tests/duty: -------------------------------------------------------------------------------- 1 | $0f0800V7fR 2 | R 3 | R 4 | R 5 | R 6 | R 7 | R 8 | R 9 | $0f1000V7fR 10 | R 11 | R 12 | R 13 | R 14 | R 15 | R 16 | R 17 | $0f1800V7fR 18 | R 19 | R 20 | R 21 | R 22 | R 23 | R 24 | R 25 | $0f2000V7fR 26 | R 27 | R 28 | R 29 | R 30 | R 31 | R 32 | R 33 | $0f0800V7fY07R 34 | R 35 | R 36 | R 37 | R 38 | R 39 | R 40 | R 41 | $0f1000V7fY0fR 42 | R 43 | R 44 | R 45 | R 46 | R 47 | R 48 | R 49 | $0f1800V7fY1fR 50 | R 51 | R 52 | R 53 | R 54 | R 55 | R 56 | R 57 | $0f2000V7fY2fR 58 | R 59 | R 60 | R 61 | R 62 | R 63 | R 64 | R 65 | -------------------------------------------------------------------------------- /src/tests/fdistort: -------------------------------------------------------------------------------- 1 | $0S1f0800V7fR 2 | R 3 | R 4 | R 5 | R 6 | R 7 | R 8 | R 9 | $0f1000V7fR 10 | R 11 | R 12 | R 13 | R 14 | R 15 | R 16 | R 17 | $0f1800V7fR 18 | R 19 | R 20 | R 21 | R 22 | R 23 | R 24 | R 25 | $0f2000V7fR 26 | R 27 | R 28 | R 29 | R 30 | R 31 | R 32 | R 33 | $0f0800V7fI1c8000r00M4l00100110R 34 | R 35 | R 36 | R 37 | R 38 | R 39 | R 40 | R 41 | $0f1000V7fc8000R 42 | R 43 | R 44 | R 45 | R 46 | R 47 | R 48 | R 49 | $0f1800V7fc8000R 50 | R 51 | R 52 | R 53 | R 54 | R 55 | R 56 | R 57 | $0f2000V7fR 58 | R 59 | R 60 | R 61 | R 62 | R 63 | R 64 | R 65 | $0f0800V7fI1c3000rf0M4l00100110R 66 | R 67 | R 68 | R 69 | R 70 | R 71 | R 72 | R 73 | $0f1000V7fc2000R 74 | R 75 | R 76 | R 77 | R 78 | R 79 | R 80 | R 81 | $0f1800V7fc1000R 82 | R 83 | R 84 | R 85 | R 86 | R 87 | R 88 | R 89 | $0f2000V7fc2000R 90 | R 91 | R 92 | R 93 | R 94 | R 95 | R 96 | R 97 | -------------------------------------------------------------------------------- /src/tests/filter: -------------------------------------------------------------------------------- 1 | $0S4f8000V7f;;;;;; 2 | -------------------------------------------------------------------------------- /src/tests/fsweep: -------------------------------------------------------------------------------- 1 | $0f0800V7fR 2 | R 3 | R 4 | R 5 | R 6 | R 7 | R 8 | R 9 | $0f1000V7fR 10 | R 11 | R 12 | R 13 | R 14 | R 15 | R 16 | R 17 | $0f1800V7fR 18 | R 19 | R 20 | R 21 | R 22 | R 23 | R 24 | R 25 | $0f2000V7fR 26 | R 27 | R 28 | R 29 | R 30 | R 31 | R 32 | R 33 | $0f0800V7fI1c8000r7fM4l00100110R 34 | R 35 | R 36 | R 37 | R 38 | R 39 | R 40 | R 41 | $0f1000V7fc8000R 42 | R 43 | R 44 | R 45 | R 46 | R 47 | R 48 | R 49 | $0f1800V7fc8000R 50 | R 51 | R 52 | R 53 | R 54 | R 55 | R 56 | R 57 | $0f2000V7fR 58 | R 59 | R 60 | R 61 | R 62 | R 63 | R 64 | R 65 | -------------------------------------------------------------------------------- /src/tests/fuzzie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tildearrow/soundtracker/4240845b5e00510128240222ce66d87fbe71daf7/src/tests/fuzzie -------------------------------------------------------------------------------- /src/tests/psweep: -------------------------------------------------------------------------------- 1 | $0f0800V7fR 2 | R 3 | R 4 | R 5 | R 6 | R 7 | R 8 | R 9 | $0f1000V7fR 10 | R 11 | R 12 | R 13 | R 14 | R 15 | R 16 | R 17 | $0f1800V7fR 18 | R 19 | R 20 | R 21 | R 22 | R 23 | R 24 | R 25 | $0f2000V7fR 26 | R 27 | R 28 | R 29 | R 30 | R 31 | R 32 | R 33 | $0f0800V7fM2k01000006R 34 | R 35 | R 36 | R 37 | R 38 | R 39 | R 40 | R 41 | $0f1000V7fR 42 | R 43 | R 44 | R 45 | R 46 | R 47 | R 48 | R 49 | $0f1800V7fR 50 | R 51 | R 52 | R 53 | R 54 | R 55 | R 56 | R 57 | $0f2000V7fR 58 | R 59 | R 60 | R 61 | R 62 | R 63 | R 64 | R 65 | $0f0800V7fM2k020081ffR 66 | R 67 | R 68 | R 69 | R 70 | R 71 | R 72 | R 73 | $0f1000V7fR 74 | R 75 | R 76 | R 77 | R 78 | R 79 | R 80 | R 81 | $0f1800V7fR 82 | R 83 | R 84 | R 85 | R 86 | R 87 | R 88 | R 89 | $0f2000V7fR 90 | R 91 | R 92 | R 93 | R 94 | R 95 | R 96 | R 97 | $0f0800V7fM2k40007f04R 98 | R 99 | R 100 | R 101 | R 102 | R 103 | R 104 | R 105 | $0f1000V7fR 106 | R 107 | R 108 | R 109 | R 110 | R 111 | R 112 | R 113 | $0f1800V7fR 114 | R 115 | R 116 | R 117 | R 118 | R 119 | R 120 | R 121 | $0f2000V7fR 122 | R 123 | R 124 | R 125 | R 126 | R 127 | R 128 | R 129 | -------------------------------------------------------------------------------- /src/tests/shape: -------------------------------------------------------------------------------- 1 | $0f0800V7fRRRRRRRR 2 | $0f1000V7fRRRRRRRR 3 | $0f1800V7fRRRRRRRR 4 | $0f2000V7fRRRRRRRR 5 | $0S1f0800V7fRRRRRRRR 6 | $0f1000V7fRRRRRRRR 7 | $0f1800V7fRRRRRRRR 8 | $0f2000V7fRRRRRRRR 9 | $0S2f0800V7fRRRRRRRR 10 | $0f1000V7fRRRRRRRR 11 | $0f1800V7fRRRRRRRR 12 | $0f2000V7fRRRRRRRR 13 | $0S3f0800V7fRRRRRRRR 14 | $0f1000V7fRRRRRRRR 15 | $0f1800V7fRRRRRRRR 16 | $0f2000V7fRRRRRRRR 17 | $0S4f0800V7fRRRRRRRR 18 | $0f1000V7fRRRRRRRR 19 | $0f1800V7fRRRRRRRR 20 | $0f2000V7fRRRRRRRR 21 | $0S5f0800V7fRRRRRRRR 22 | $0f1000V7fRRRRRRRR 23 | $0f1800V7fRRRRRRRR 24 | $0f2000V7fRRRRRRRR 25 | $0Y0ff0800V7fRRRRRRRR 26 | $0f1000V7fRRRRRRRR 27 | $0f1800V7fRRRRRRRR 28 | $0f2000V7fRRRRRRRR 29 | $0Y1ff0400V7fRRRRRRRR 30 | $0f0800V7fRRRRRRRR 31 | $0f0c00V7fRRRRRRRR 32 | $0f1000V7fRRRRRRRR 33 | $0Y2ff0200V7fRRRRRRRR 34 | $0f0400V7fRRRRRRRR 35 | $0f0600V7fRRRRRRRR 36 | $0f0800V7fRRRRRRRR 37 | $0S6f0800V7fRRRRRRRR 38 | $0f1000V7fRRRRRRRR 39 | $0f1800V7fRRRRRRRR 40 | $0f2000V7fRRRRRRRR 41 | $0S7f0800V7fRRRRRRRR 42 | $0f1000V7fRRRRRRRR 43 | $0f1800V7fRRRRRRRR 44 | $0f2000V7fRRRRRRRR 45 | -------------------------------------------------------------------------------- /src/tests/vsweep: -------------------------------------------------------------------------------- 1 | $0f0800V7fR 2 | R 3 | R 4 | R 5 | R 6 | R 7 | R 8 | R 9 | $0f1000V7fR 10 | R 11 | R 12 | R 13 | R 14 | R 15 | R 16 | R 17 | $0f1800V7fR 18 | R 19 | R 20 | R 21 | R 22 | R 23 | R 24 | R 25 | $0f2000V7fR 26 | R 27 | R 28 | R 29 | R 30 | R 31 | R 32 | R 33 | $0f0800V7fM1v00880100R 34 | R 35 | R 36 | R 37 | R 38 | R 39 | R 40 | R 41 | $0f1000V7fR 42 | R 43 | R 44 | R 45 | R 46 | R 47 | R 48 | R 49 | $0f1800V7fR 50 | R 51 | R 52 | R 53 | R 54 | R 55 | R 56 | R 57 | $0f2000V7fR 58 | R 59 | R 60 | R 61 | R 62 | R 63 | R 64 | R 65 | $0f0800V7fM1v00084100R 66 | R 67 | R 68 | R 69 | R 70 | R 71 | R 72 | R 73 | $0f1000V7fR 74 | R 75 | R 76 | R 77 | R 78 | R 79 | R 80 | R 81 | $0f1800V7fR 82 | R 83 | R 84 | R 85 | R 86 | R 87 | R 88 | R 89 | $0f2000V7fR 90 | R 91 | R 92 | R 93 | R 94 | R 95 | R 96 | R 97 | $0f0400V7fM1v0010c300R 98 | R 99 | R 100 | R 101 | R 102 | R 103 | R 104 | R 105 | R 106 | R 107 | R 108 | R 109 | R 110 | R 111 | R 112 | R 113 | R 114 | R 115 | R 116 | R 117 | R 118 | R 119 | R 120 | -------------------------------------------------------------------------------- /src/tracker.h: -------------------------------------------------------------------------------- 1 | // new tracker code 2 | #ifndef _TRACKER_H 3 | #define _TRACKER_H 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef HAVE_GUI 9 | #include 10 | #else 11 | #include 12 | #endif 13 | #ifdef ANDROID 14 | #define ANDRO 15 | #endif 16 | #ifdef _WIN32 17 | #include 18 | #include 19 | #else 20 | #if !defined(__APPLE__) && !defined(ANDROID) 21 | #include 22 | #endif 23 | #include 24 | #include 25 | #endif 26 | #ifdef __APPLE__ 27 | extern "C" { 28 | #include "nsstub.h" 29 | } 30 | #endif 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "fextra.h" 39 | #include "soundchip.h" 40 | 41 | // format version 42 | #define TRACKER_VER 153 43 | // actual version 44 | #define TRACKER_FULLVER "v1.0pre" 45 | 46 | #define minval(a,b) (((a)<(b))?(a):(b)) // for Linux compatibility 47 | #define maxval(a,b) (((a)>(b))?(a):(b)) // for Linux compatibility 48 | 49 | #include "utfutils.h" 50 | 51 | bool PIR(float x1, float y1, float x2, float y2, float checkx, float checky); 52 | 53 | enum SupportedFormats { 54 | FormatUnknown=-1, 55 | 56 | FormatTRACK=0, 57 | FormatTRACKINS, 58 | FormatMOD, 59 | FormatS3M, 60 | FormatIT, 61 | FormatXM, 62 | FormatAudio, 63 | }; 64 | 65 | struct Point { 66 | float x, y; 67 | }; 68 | 69 | struct Color { 70 | float r, g, b, a; 71 | Color(): 72 | r(0), 73 | g(0), 74 | b(0), 75 | a(1) {} 76 | explicit Color(unsigned char re, unsigned char gr, unsigned char bl): 77 | r((float)re/255.0f), 78 | g((float)gr/255.0f), 79 | b((float)bl/255.0f), 80 | a(1.0f) {} 81 | explicit Color(unsigned char re, unsigned char gr, unsigned char bl, unsigned char al): 82 | r((float)re/255.0f), 83 | g((float)gr/255.0f), 84 | b((float)bl/255.0f), 85 | a((float)al/255.0f) {} 86 | explicit Color(float re, float gr, float bl): 87 | r(re), 88 | g(gr), 89 | b(bl), 90 | a(1.0f) {} 91 | explicit Color(float re, float gr, float bl, float al): 92 | r(re), 93 | g(gr), 94 | b(bl), 95 | a(al) {} 96 | }; 97 | 98 | struct Texture { 99 | SDL_Texture* actual; 100 | int w, h; 101 | Texture(): actual(NULL), w(0), h(0) {} 102 | }; 103 | 104 | enum UIType { 105 | UIClassic=0, 106 | UIModern, 107 | UIMobile 108 | }; 109 | 110 | extern int scrW, scrH; 111 | 112 | class PopupBox { 113 | string title; 114 | string content; 115 | 116 | bool show; 117 | 118 | public: 119 | void hide() { 120 | show=false; 121 | } 122 | 123 | bool isVisible() { 124 | return show; 125 | } 126 | 127 | void draw() { 128 | } 129 | 130 | //content("Lorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem.") 131 | 132 | PopupBox(string t, string c): 133 | title(t), 134 | content(c), 135 | show(true) {} 136 | 137 | PopupBox(bool s): 138 | title("Message"), 139 | content("Lorem ipsum, quia dolor sit, amet."), 140 | show(s) {} 141 | 142 | 143 | PopupBox(): 144 | title("Message"), 145 | content("Lorem ipsum, quia dolor sit, amet."), 146 | show(true) {} 147 | }; 148 | 149 | // NEW STUFF BEGIN // 150 | struct LegacyInstrument { 151 | char name[32]; 152 | unsigned char id, pcmMult, activeEnv; 153 | unsigned char env[8]; 154 | unsigned char noteOffset; 155 | unsigned char FPt, FPR, DFM, LFO; 156 | unsigned char vol; 157 | signed char pitch; 158 | unsigned short pcmLen, filterH; 159 | unsigned char res; 160 | unsigned char pcmPos[2]; // alignment 161 | unsigned char pcmLoop[2]; 162 | unsigned char FTm; 163 | unsigned short ver; 164 | unsigned char flags, RMf; 165 | }; 166 | 167 | struct Instrument { 168 | char name[32]; 169 | unsigned char id, pcmMult; 170 | short volMacro, cutMacro, resMacro, pitchMacro; 171 | unsigned char unused1, noteOffset; 172 | unsigned char FPt, FPR, filterMode, LFO; 173 | unsigned char vol; 174 | signed char pitch; 175 | unsigned short pcmLen, filterH; 176 | unsigned char res, FTm; 177 | unsigned short pcmPos; 178 | unsigned short pcmLoop; 179 | unsigned short ver; 180 | unsigned char flags, RMf; 181 | short finePitchMacro, shapeMacro, dutyMacro, panMacro, filterModeMacro, volSweepMacro, freqSweepMacro, cutSweepMacro; 182 | short pcmPosMacro; 183 | unsigned short unused2; 184 | unsigned int unused3[3]; 185 | Instrument(): 186 | id(0), 187 | pcmMult(0), 188 | volMacro(-1), cutMacro(-1), resMacro(-1), pitchMacro(-1), 189 | unused1(0), 190 | noteOffset(48), 191 | FPt(0), 192 | FPR(0), 193 | filterMode(0), 194 | LFO(0), 195 | vol(64), 196 | pitch(0), 197 | pcmLen(0), 198 | filterH(0), 199 | res(0), 200 | FTm(0), 201 | pcmPos(0), 202 | pcmLoop(0), 203 | ver(TRACKER_VER), 204 | flags(0), 205 | RMf(0), 206 | finePitchMacro(-1), shapeMacro(-1), dutyMacro(-1), panMacro(-1), 207 | filterModeMacro(-1), volSweepMacro(-1), freqSweepMacro(-1), cutSweepMacro(-1), 208 | pcmPosMacro(-1), unused2(0) { 209 | memset(name,0,32); 210 | unused3[0]=0; 211 | unused3[1]=0; 212 | unused3[2]=0; 213 | } 214 | }; 215 | 216 | enum MacroCommandType { 217 | cmdEnd=0, 218 | cmdSet, 219 | cmdWait, 220 | cmdWaitRel, 221 | cmdLoop, 222 | cmdLoopRel, 223 | cmdAdd, 224 | cmdSub, 225 | 226 | cmdMax 227 | }; 228 | 229 | enum MacroIntendedUse { 230 | iuGeneric, 231 | iuShape, 232 | iuPitch, 233 | iuPan, 234 | iuVolSweep, 235 | iuOtherSweep, 236 | 237 | iuMax 238 | }; 239 | 240 | struct MacroCommand { 241 | unsigned char type; 242 | unsigned int value; 243 | MacroCommand(unsigned char t, int v, bool endTick): 244 | type(t|(endTick<<7)), 245 | value(v) {} 246 | }; 247 | 248 | struct Macro { 249 | int jumpRelease; 250 | unsigned char intendedUse; 251 | std::vector cmds; 252 | Macro(): 253 | jumpRelease(-1), 254 | intendedUse(iuGeneric) {} 255 | }; 256 | 257 | struct Pattern { 258 | unsigned short length; 259 | unsigned char data[256][32][8]; 260 | 261 | Pattern(): length(64) { 262 | memset(data,0,256*32*8); 263 | } 264 | }; 265 | 266 | struct Song { 267 | char header[8]; 268 | unsigned short version; 269 | unsigned char insC, patC, orders, speed, flags, tempo; 270 | char name[32]; 271 | unsigned char DFM, channels; 272 | unsigned short macrosC; 273 | unsigned char globalVol, globalPan; 274 | unsigned short pcmPtr[2]; 275 | unsigned short commentPtr[2]; 276 | signed char detune; 277 | unsigned char len; 278 | unsigned char defaultVol[32]; 279 | signed char defaultPan[32]; 280 | unsigned char order[256]; 281 | Instrument* ins[256]; 282 | Pattern* pat[256]; 283 | std::vector macros; 284 | 285 | Pattern* getPattern(unsigned char num, bool create); 286 | 287 | Song(); 288 | ~Song(); 289 | }; 290 | 291 | class Player; 292 | 293 | class MacroStatus { 294 | Macro* macro; 295 | int pos, waitTime; 296 | bool released; 297 | public: 298 | bool hasChanged; 299 | unsigned int value; 300 | 301 | void next(); 302 | void release(); 303 | void load(Macro* m); 304 | MacroStatus(); 305 | }; 306 | 307 | struct ChannelStatus { 308 | bool active, noteOn; 309 | float note; 310 | short instr; 311 | short vol, channelVol; 312 | short envVol; 313 | short finePitch; 314 | unsigned char fx, fxVal; 315 | unsigned char arp, arpValue; 316 | unsigned char volSlide; 317 | signed char channelPan; 318 | unsigned char cutTimer, rowDelay; 319 | 320 | bool volChanged, freqChanged, panChanged; 321 | 322 | float slideSpeed; 323 | float portaSpeed, portaTarget; 324 | 325 | unsigned char vibPos, vibSpeed, vibDepth; 326 | float vibValue; 327 | 328 | unsigned char tremPos, tremSpeed, tremDepth; 329 | float tremValue; 330 | 331 | unsigned char panbPos, panbSpeed, panbDepth; 332 | float panbValue; 333 | 334 | MacroStatus macroVol; 335 | MacroStatus macroCut; 336 | MacroStatus macroRes; 337 | MacroStatus macroDuty; 338 | MacroStatus macroShape; 339 | MacroStatus macroPitch; 340 | MacroStatus macroFinePitch; 341 | MacroStatus macroPan; 342 | MacroStatus macroFilterMode; 343 | MacroStatus macroVolSweep; 344 | MacroStatus macroFreqSweep; 345 | MacroStatus macroCutSweep; 346 | MacroStatus macroPCM; 347 | 348 | ChannelStatus(): 349 | active(false), 350 | noteOn(false), 351 | note(0), 352 | instr(0), 353 | vol(0), 354 | channelVol(128), 355 | envVol(255), 356 | finePitch(0), 357 | fx(0), 358 | fxVal(0), 359 | arpValue(0), 360 | volSlide(0), 361 | channelPan(0), 362 | cutTimer(0), 363 | rowDelay(0), 364 | volChanged(false), 365 | freqChanged(false), 366 | panChanged(false), 367 | slideSpeed(0), 368 | portaSpeed(0), 369 | portaTarget(0), 370 | vibPos(0), 371 | vibSpeed(0), 372 | vibDepth(0), 373 | vibValue(0), 374 | tremPos(0), 375 | tremSpeed(0), 376 | tremDepth(0), 377 | tremValue(0), 378 | panbPos(0), 379 | panbSpeed(0), 380 | panbDepth(0), 381 | panbValue(0) {} 382 | }; 383 | 384 | struct ScheduledNote { 385 | int chan, ins, note; 386 | ScheduledNote(int c, int i, int n): 387 | chan(c), 388 | ins(i), 389 | note(n) {} 390 | }; 391 | 392 | class Player { 393 | Song* song; 394 | soundchip* chip; 395 | 396 | std::queue scheduledNotes; 397 | 398 | public: 399 | int pat, step, tick, playMode; 400 | int speed, tempo, nextJump, nextJumpStep; 401 | int patLoopPos, patLoopCount; 402 | bool ntsc; 403 | ChannelStatus chan[32]; 404 | bool channelMask[32]; 405 | 406 | unsigned int getNoteFreq(float note); 407 | unsigned int getNotePeriod(float note); 408 | 409 | float offsetNote(float note, unsigned char off); 410 | 411 | void testNoteOn(int channel, int ins, int note); 412 | void testNoteOff(int channel); 413 | void noteOn(int channel, int note); 414 | void noteOff(int channel); 415 | void noteCut(int channel); 416 | void noteAftertouch(int channel, int val); 417 | void noteProgramChange(int channel, int val); 418 | void notePanChange(int channel, signed char val); 419 | 420 | void processChanRow(Pattern* pat, int channel); 421 | void nextRow(); 422 | 423 | void maskChannel(int channel, bool mask); 424 | void toggleChannel(int channel); 425 | 426 | void update(); 427 | void reset(); 428 | void panic(); 429 | void play(); 430 | void stop(); 431 | 432 | void setSong(Song* s); 433 | void bindChips(soundchip* s); 434 | 435 | Player(); 436 | }; 437 | 438 | // NEW STUFF END // 439 | #endif 440 | -------------------------------------------------------------------------------- /src/unifontfull.h: -------------------------------------------------------------------------------- 1 | extern const unsigned char unifont_bin[2097152]; 2 | extern const unsigned char unifont_siz[8192]; 3 | -------------------------------------------------------------------------------- /src/utfutils.cpp: -------------------------------------------------------------------------------- 1 | #include "tracker.h" 2 | 3 | int decodeUTF8(const unsigned char* data, char& len) { 4 | int ret=0xfffd; 5 | if (data[0]<0x80) { 6 | ret=data[0]; 7 | len=1; 8 | } else if (data[0]<0xc0) { 9 | ret=0xfffd; // invalid 10 | len=1; 11 | } else if (data[0]<0xe0) { 12 | if (data[1]>=0x80 && data[1]<0xc0) { 13 | len=2; 14 | ret=((data[0]&31)<<6)| 15 | (data[1]&63); 16 | } else len=1; 17 | } else if (data[0]<0xf0) { 18 | if (data[1]>=0x80 && data[1]<0xc0) { 19 | if (data[2]>=0x80 && data[2]<0xc0) { 20 | len=3; 21 | ret=((data[0]&15)<<12)| 22 | ((data[1]&63)<<6)| 23 | (data[2]&63); 24 | } else len=2; 25 | } else len=1; 26 | } else if (data[0]<0xf5) { 27 | if (data[1]>=0x80 && data[1]<0xc0) { 28 | if (data[2]>=0x80 && data[2]<0xc0) { 29 | if (data[3]>=0x80 && data[3]<0xc0) { 30 | len=4; 31 | ret=((data[0]&7)<<18)| 32 | ((data[1]&63)<<12)| 33 | ((data[2]&63)<<6)| 34 | (data[3]&63); 35 | } else len=3; 36 | } else len=2; 37 | } else len=1; 38 | } else { 39 | len=1; 40 | return 0xfffd; 41 | } 42 | 43 | if ((ret>=0xd800 && ret<=0xdfff) || ret>=0x110000) return 0xfffd; 44 | return ret; 45 | } 46 | 47 | size_t utf8len(const char* s) { 48 | size_t p=0; 49 | size_t r=0; 50 | char cl; 51 | while (s[p]!=0) { 52 | r++; 53 | decodeUTF8((const unsigned char*)&s[p],cl); 54 | p+=cl; 55 | } 56 | return r; 57 | } 58 | 59 | char utf8csize(const unsigned char* c) { 60 | char ret; 61 | decodeUTF8(c,ret); 62 | return ret; 63 | } 64 | 65 | wstring utf8To16(const char* s) { 66 | wstring ret; 67 | int ch, p; 68 | char chs; 69 | p=0; 70 | while (s[p]!=0) { 71 | ch=decodeUTF8((const unsigned char*)&s[p],chs); 72 | ret+=(unsigned short)ch; 73 | p+=chs; 74 | } 75 | return ret; 76 | } 77 | 78 | string utf16To8(const wchar_t* s) { 79 | string ret; 80 | for (size_t i=0; i>6)&31)); 85 | ret+=(0x80+((s[i])&63)); 86 | } else { 87 | ret+=(0xe0+((s[i]>>12)&15)); 88 | ret+=(0x80+((s[i]>>6)&63)); 89 | ret+=(0x80+((s[i])&63)); 90 | 91 | } 92 | } 93 | return ret; 94 | } 95 | -------------------------------------------------------------------------------- /src/utfutils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTFUTILS_H 2 | #define _UTFUTILS_H 3 | #include 4 | 5 | typedef std::string string; 6 | typedef std::wstring wstring; 7 | #define S(x) string(x) 8 | 9 | size_t utf8len(const char* s); 10 | size_t utf8clen(const char* s); 11 | size_t utf8pos(const char* s, size_t inpos); 12 | size_t utf8cpos(const char* s, size_t inpos); 13 | size_t utf8findcpos(const char* s, float inpos); 14 | char utf8csize(const unsigned char* c); 15 | 16 | wstring utf8To16(const char* in); 17 | string utf16To8(const wchar_t* in); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/winMain.cpp: -------------------------------------------------------------------------------- 1 | #include "utfutils.h" 2 | 3 | int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, PSTR args, int state) { 4 | int argc=0; 5 | wchar_t** argw=CommandLineToArgvW(GetCommandLineW(),&argc); 6 | char** argv=new char*[argc+1]; 7 | argv[argc]=NULL; 8 | for (int i=0; i 2 | #include 3 | 4 | FILE* f; 5 | FILE* out; 6 | FILE* sout; 7 | 8 | char str[256]; 9 | 10 | unsigned char glyph[64]; 11 | int glyphSize; 12 | 13 | int glyphIndex; 14 | 15 | unsigned char glyphSizes[8192]; 16 | 17 | char char2hex(char c) { 18 | if (c>='0' && c<='9') { 19 | return c-'0'; 20 | } 21 | if (c>='A' && c<='F') { 22 | return 10+c-'A'; 23 | } 24 | if (c>='a' && c<='f') { 25 | return 10+c-'a'; 26 | } 27 | return -1; 28 | } 29 | 30 | int main(int argc, char** argv) { 31 | if (argc<4) { 32 | printf("usage: %s in out sizeout\n",argv[0]); 33 | return 1; 34 | } 35 | f=fopen(argv[1],"r"); 36 | if (f==NULL) { 37 | perror("in"); 38 | return 1; 39 | } 40 | 41 | out=fopen(argv[2],"wb"); 42 | if (out==NULL) { 43 | perror("out"); 44 | return 1; 45 | } 46 | 47 | sout=fopen(argv[3],"wb"); 48 | if (sout==NULL) { 49 | perror("out"); 50 | return 1; 51 | } 52 | 53 | memset(glyphSizes,0,8192); 54 | 55 | int h=0; 56 | while (!feof(f)) { 57 | fgets(str,255,f); 58 | glyphIndex=0; 59 | glyphSize=0; 60 | h=0; 61 | do { 62 | if (str[h]==':') break; 63 | glyphIndex<<=4; 64 | glyphIndex|=char2hex(str[h]); 65 | } while (++h); 66 | if (glyphIndex>0xffff) { 67 | break; 68 | } 69 | memset(glyph,0,32); 70 | h++; 71 | do { 72 | if (str[h]=='\n' || str[h]==0) break; 73 | glyph[glyphSize>>1]<<=4; 74 | glyph[glyphSize>>1]|=char2hex(str[h]); 75 | glyphSize++; 76 | } while (++h); 77 | glyphSize>>=1; 78 | // write a 16x16 glyph regardless 79 | fseek(out,glyphIndex*32,SEEK_SET); 80 | if (glyphSize==32) { 81 | fwrite(glyph,1,32,out); 82 | } else { 83 | for (int i=0; i<16; i++) { 84 | fputc(glyph[i],out); 85 | fputc(0,out); 86 | } 87 | } 88 | 89 | if (glyphSize>16) { 90 | glyphSizes[glyphIndex>>3]|=(1<<(glyphIndex&7)); 91 | } else { 92 | glyphSizes[glyphIndex>>3]&=~(1<<(glyphIndex&7)); 93 | } 94 | } 95 | fseek(out,2097151,SEEK_SET); 96 | fputc(0,out); 97 | 98 | fwrite(glyphSizes,1,8192,sout); 99 | 100 | fclose(f); 101 | fclose(out); 102 | fclose(sout); 103 | return 0; 104 | } 105 | --------------------------------------------------------------------------------