shithub: dumb

Download patch

ref: 1fb50f404149c0646a2fffffabcdb1de6e8e0c64
parent: da7a02097996668a55fb5141df190bf324320763
author: Tuomas Virtanen <katajakasa@gmail.com>
date: Sun Nov 15 17:25:29 EST 2015

Add dumbout example

This is a rewritten dumbout example. It maintains some backwards compatibility with most of the input flags, but errors and messages have mostly been rewritten.

diff: cannot open b/dumb/cmake/cmake-scripts//null: file does not exist: 'b/dumb/cmake/cmake-scripts//null' diff: cannot open b/dumb/examples//null: file does not exist: 'b/dumb/examples//null'
--- a/dumb/cmake/CMakeLists.txt
+++ b/dumb/cmake/CMakeLists.txt
@@ -1,6 +1,10 @@
 cmake_minimum_required(VERSION 2.6)
 project(libdumb C)
 
+set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake-scripts)
+
+option(BUILD_EXAMPLES "Build example binaries" ON)
+
 set(CMAKE_C_FLAGS "-Wall -DDUMB_DECLARE_DEPRECATED -D_USE_SSE -msse -Wno-unused-variable -Wno-unused-but-set-variable")
 set(CMAKE_C_FLAGS_DEBUG "-ggdb -DDEBUGMODE=1 -D_DEBUG")
 set(CMAKE_C_FLAGS_RELEASE "-ffast-math -O2 -DNDEBUG")
@@ -10,6 +14,12 @@
 link_directories(${CMAKE_CURRENT_BINARY_DIR})
 include_directories(../include/)
 
+if(BUILD_EXAMPLES)
+    find_package(argtable2)
+else()
+    message(STATUS "Not building examples")
+endif()
+
 SET(SOURCES
     ../src/core/unload.c
     ../src/core/rendsig.c
@@ -105,6 +115,15 @@
 add_library(dumb ${SOURCES})
 set_target_properties(dumb PROPERTIES DEBUG_POSTFIX d)
 
+set(EXAMPLE_TARGETS "")
+
+if(BUILD_EXAMPLES)
+    add_executable(dumbout ../examples/dumbout.c)
+    target_link_libraries(dumbout ${ARGTABLE2_LIBRARY} dumb)
+    include_directories(${ARGTABLE2_INCLUDE_DIR} "../examples/")
+    set(EXAMPLE_TARGETS ${EXAMPLE_TARGETS} "dumbout")
+endif()
+
 # Make sure the dylib install name path is set on OSX so you can include dumb in app bundles
 IF(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
     set_target_properties(dumb PROPERTIES INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib)
@@ -113,7 +132,7 @@
 target_link_libraries(dumb m)
 
 INSTALL(FILES ${INSTALL_HEADERS} DESTINATION include/)
-INSTALL(TARGETS dumb
+INSTALL(TARGETS dumb ${EXAMPLE_TARGETS}
     RUNTIME DESTINATION bin
     LIBRARY DESTINATION lib${LIB_SUFFIX}
     ARCHIVE DESTINATION lib${LIB_SUFFIX}
--- /dev/null
+++ b/dumb/cmake/cmake-scripts/Findargtable2.cmake
@@ -1,0 +1,27 @@
+SET(ARGTABLE2_SEARCH_PATHS
+        /usr/local
+        /usr
+        /opt
+)
+
+FIND_PATH(ARGTABLE2_INCLUDE_DIR argtable2.h
+        HINTS
+        PATH_SUFFIXES include
+        PATHS ${ARGTABLE2_SEARCH_PATHS}
+)
+FIND_LIBRARY(ARGTABLE2_LIBRARY argtable2
+        HINTS
+        PATH_SUFFIXES lib64 lib
+        PATHS ${ARGTABLE2_SEARCH_PATHS}
+)
+
+if(ARGTABLE2_INCLUDE_DIR AND ARGTABLE2_LIBRARY)
+    set(ARGTABLE2_FOUND TRUE)
+endif(ARGTABLE2_INCLUDE_DIR AND ARGTABLE2_LIBRARY)
+
+
+if(ARGTABLE2_FOUND)
+    message(STATUS "Found Argtable2: ${ARGTABLE2_LIBRARY}")
+else(ARGTABLE2_FOUND)
+    message(WARNING "Could not find Argtable2")
+endif(ARGTABLE2_FOUND)
\ No newline at end of file
--- /dev/null
+++ b/dumb/examples/dumbout.c
@@ -1,0 +1,238 @@
+#include <argtable2.h>
+#include <dumb.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+static const int endian_test = 1;
+#define is_bigendian() ((*(char*)&endian_test) == 0)
+
+enum ENDIANNESS {
+    LITTLE_ENDIAN = 0,
+    BIG_ENDIAN
+};
+
+typedef struct {
+    DUH_SIGRENDERER *renderer;
+    DUH *src;
+    FILE *dst;
+    float delta;
+    int bufsize;
+    bool is_stdout;
+} streamer_t;
+
+typedef struct {
+    int bits;
+    int endianness;
+    int is_unsigned;
+    int freq;
+    int quality;
+    int n_channels;
+    float volume;
+    float delay;
+    const char *input;
+    char *output;
+} settings_t;
+
+
+int main(int argc, char *argv[]) {
+    int retcode = 1;
+    int nerrors = 0;
+    streamer_t streamer;
+    settings_t settings;
+    memset(&streamer, 0, sizeof(streamer_t));
+    memset(&settings, 0, sizeof(settings_t));
+
+    // Defaults
+    settings.freq = 44100;
+    settings.n_channels = 2;
+    settings.bits = 16;
+    settings.endianness = LITTLE_ENDIAN;
+    settings.is_unsigned = false;
+    settings.volume = 1.0f;
+    settings.delay = 0.0f;
+    settings.quality = DUMB_RQ_CUBIC;
+
+    // commandline argument parser options
+    struct arg_lit *arg_help = arg_lit0("h", "help", "print this help and exits");
+    struct arg_dbl *arg_delay = arg_dbl0("d", "delay", "<delay>", "sets the initial delay in seconds (0.0 to 64.0, default 0.0)");
+    struct arg_dbl *arg_volume = arg_dbl0("v", "volume", "<volume", "sets the output volume (-8.0 to +8.0, default 1.0)");
+    struct arg_int *arg_samplerate = arg_int0("s", "samplerate", "<freq>", "sets the sampling rate (default 44100)");
+    struct arg_int *arg_quality = arg_int0("r", "quality", "<quality>", "specify the resampling quality to use");
+    struct arg_lit *arg_mono = arg_lit0("m", "mono", "generate mono output instead of stereo");
+    struct arg_lit *arg_bigendian = arg_lit0("b", "bigendian", "generate big-endian data instead of little-endian");
+    struct arg_lit *arg_eight = arg_lit0("8", "eight", "generate 8-bit instead of 16-bit");
+    struct arg_lit *arg_unsigned = arg_lit0("u", "unsigned", "generate unsigned output instead of signed");
+    struct arg_file *arg_output = arg_file0("o", "output", "<file>", "output file");
+    struct arg_file *arg_input = arg_file1(NULL, NULL, "<file>", "input module file");
+    struct arg_end *arg_fend = arg_end(20);
+    void* argtable[] = {arg_help, arg_input, arg_output, arg_delay, arg_volume, arg_samplerate, arg_quality,
+                        arg_mono, arg_bigendian, arg_eight, arg_unsigned, arg_fend};
+    const char* progname = "dumbout";
+
+    // Make sure everything got allocated
+    if(arg_nullcheck(argtable) != 0) {
+        fprintf(stderr, "%s: insufficient memory\n", progname);
+        goto exit_0;
+    }
+
+    // Parse inputs
+    nerrors = arg_parse(argc, argv, argtable);
+
+    // Handle help
+    if(arg_help->count > 0) {
+        fprintf(stderr, "Usage: %s", progname);
+        arg_print_syntax(stderr, argtable, "\n");
+        fprintf(stderr, "\nArguments:\n");
+        arg_print_glossary(stderr, argtable, "%-25s %s\n");
+        goto exit_0;
+    }
+
+    // Handle errors
+    if(nerrors > 0) {
+        arg_print_errors(stderr, arg_fend, progname);
+        fprintf(stderr, "Try '%s --help' for more information.\n", progname);
+        goto exit_0;
+    }
+
+    // Get input and output filenames
+    settings.input = arg_input->filename[0];
+    if(arg_output->count > 0) {
+        settings.output = malloc(strlen(arg_output->filename[0])+1);
+        strcpy(settings.output, arg_output->filename[0]);
+    } else {
+        settings.output = malloc(strlen(arg_output->basename[0])+5);
+        sprintf(settings.output, "%s%s", arg_output->basename[0], ".pcm");
+    }
+
+    // Handle the switch options
+    if(arg_bigendian->count > 0) { settings.endianness = BIG_ENDIAN; }
+    if(arg_eight->count > 0) { settings.bits = 8; }
+    if(arg_unsigned->count > 0) { settings.is_unsigned = true; }
+    if(arg_mono->count > 0) { settings.n_channels = 1; }
+
+    if(arg_delay->count > 0) {
+        settings.delay = arg_delay->dval[0];
+        if(settings.delay < 0.0f || settings.delay >= 64.0f) {
+            fprintf(stderr, "Initial delay must be between 0.0f and 64.0f.\n");
+            goto exit_0;
+        }
+    }
+
+    if(arg_volume->count > 0) {
+        settings.volume = arg_volume->dval[0];
+        if(settings.volume < -8.0f || settings.volume > 8.0f) {
+            fprintf(stderr, "Volume must be between -8.0f and 8.0f.\n");
+            goto exit_0;
+        }
+    }
+
+    if(arg_samplerate->count > 0) {
+        settings.freq = arg_samplerate->ival[0];
+        if(settings.freq < 1 || settings.freq > 96000) {
+            fprintf(stderr, "Sampling rate must be between 1 and 96000.\n");
+            goto exit_0;
+        }
+    }
+
+    if(arg_quality->count > 0) {
+        settings.quality = arg_quality->ival[0];
+        if(settings.quality < 0 || settings.quality >= DUMB_RQ_N_LEVELS) {
+            fprintf(stderr, "Quality must be between %d and %d.\n", 0, DUMB_RQ_N_LEVELS-1);
+            goto exit_0;
+        }
+    }
+
+    // dumb settings stuff
+    dumb_register_stdfiles();
+
+    // Load source
+    streamer.src = dumb_load_any(settings.input, 0, 0);
+    if(!streamer.src) {
+        fprintf(stderr, "Unable to load file %s for playback!\n", settings.input);
+        goto exit_0;
+    }
+
+    // Set up playback
+    streamer.renderer = duh_start_sigrenderer(streamer.src, 0, settings.n_channels, 0);
+    streamer.delta = 65536.0f / settings.freq;
+    streamer.bufsize = 4096 * (settings.bits / 8) * settings.n_channels;
+
+    // Stop producing samples on module end
+    DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(streamer.renderer);
+    dumb_it_set_loop_callback(itsr, &dumb_it_callback_terminate, NULL);
+    dumb_it_set_xm_speed_zero_callback(itsr, &dumb_it_callback_terminate, NULL);
+    dumb_it_set_resampling_quality(itsr, settings.quality);
+
+    // Open output
+    if(strcmp(settings.output, "-") == 0) {
+        streamer.dst = stdout;
+        streamer.is_stdout = true;
+    } else {
+        streamer.dst = fopen(settings.output, "wb");
+        streamer.is_stdout = false;
+        if(!streamer.dst) {
+            fprintf(stderr, "Could not open output file %s!", settings.output);
+            goto exit_1;
+        }
+    }
+
+    // Loop as long as we have data to play
+    bool run = true;
+    char *buffer = malloc(streamer.bufsize);
+    int read_samples;
+    int read_bytes;
+
+    // If output endianness is different than machine endianness, and output is 16 bits, reorder bytes.
+    int switch_endianness = ((is_bigendian() && settings.endianness == LITTLE_ENDIAN) ||
+                            (!is_bigendian() && settings.endianness == BIG_ENDIAN)) && settings.bits == 16;
+
+    // Loop until we have nothing to loop through. Dumb will stop giving out bytes when the file is done.
+    while(run) {
+        read_samples = duh_render(streamer.renderer,
+                                  settings.bits,
+                                  (int)settings.is_unsigned,
+                                  settings.volume,
+                                  streamer.delta,
+                                  4096, buffer);
+        read_bytes = read_samples * (settings.bits / 8) * settings.n_channels;
+
+        // switch endianness if required
+        if(switch_endianness) {
+            char tmp;
+            for(int i = 0; i < read_bytes / 2; i++) {
+                tmp = buffer[i*2+0];
+                buffer[i*2+0] = buffer[i*2+1];
+                buffer[i*2+1] = tmp;
+            }
+        }
+
+        // Write to output stream and flush if it happens to be stdout
+        fwrite(buffer, 1, read_bytes, streamer.dst);
+        if(streamer.is_stdout) {
+            fflush(streamer.dst);
+        }
+        run = (read_samples > 0);
+    }
+    free(buffer);
+
+    // We made it this far without crashing, so let's just exit with no error :)
+    retcode = 0;
+
+    if(!streamer.is_stdout && streamer.dst) {
+        fclose(streamer.dst);
+    }
+
+exit_1:
+    if(streamer.renderer) {
+        duh_end_sigrenderer(streamer.renderer);
+    }
+    if(streamer.src) {
+        unload_duh(streamer.src);
+    }
+
+exit_0:
+    free(settings.output);
+    arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0]));
+    return retcode;
+}