├── .gitignore ├── test ├── tiny-sine.ogg ├── thread_safe_queue_test.hpp ├── ring_buffer_test.hpp ├── ordered_map_file_test.hpp ├── thread_safe_queue_test.cpp ├── valgrind.supp ├── ring_buffer_test.cpp └── ordered_map_file_test.cpp ├── assets ├── img │ ├── null.png │ ├── play_head.png │ └── font-awesome │ │ ├── check.png │ │ ├── file.png │ │ ├── music.png │ │ ├── plug.png │ │ ├── folder.png │ │ ├── arrow-down.png │ │ ├── arrow-left.png │ │ ├── arrow-up.png │ │ ├── caret-down.png │ │ ├── microphone.png │ │ ├── volume-up.png │ │ ├── arrow-right.png │ │ ├── caret-right.png │ │ ├── folder-open.png │ │ ├── minus-square.png │ │ ├── plus-square.png │ │ └── exclamation-circle.png └── font │ └── OpenSans-Regular.ttf ├── src ├── glfw.hpp ├── freetype.hpp ├── glm.hpp ├── synth.hpp ├── crc32.hpp ├── resample.hpp ├── delay.hpp ├── warning.hpp ├── dragged_sample_file.hpp ├── mixer_node.hpp ├── id_map.hpp ├── id_map.cpp ├── debug_gl.hpp ├── random.hpp ├── config.h.in ├── resource_bundle.hpp ├── main.cpp ├── ffmpeg.hpp ├── sha_256_hasher.hpp ├── static_geometry.hpp ├── png_image.hpp ├── assets.json ├── device_id.hpp ├── texture.hpp ├── drag_event.hpp ├── spacer_widget.hpp ├── atomics.hpp ├── atomic_double.hpp ├── alpha_texture.hpp ├── warning.cpp ├── mouse_event.hpp ├── render_job.hpp ├── sha_256_hasher.cpp ├── project_props_widget.hpp ├── font_size.hpp ├── resource_bundle.cpp ├── static_geometry.cpp ├── sunken_box.hpp ├── device_id.cpp ├── spritesheet.hpp ├── sequencer_widget.hpp ├── ring_buffer.hpp ├── button_widget.hpp ├── mixer_widget.hpp ├── shader_program_manager.hpp ├── sort_key.hpp ├── random.cpp ├── shader_program.hpp ├── atomic_value.hpp ├── scroll_bar_widget.hpp ├── util.cpp ├── ring_buffer.cpp ├── select_widget.hpp ├── render_widget.hpp ├── render_job.cpp ├── color.hpp ├── string.hpp ├── label.hpp ├── widget.hpp ├── font_size.cpp ├── button_widget.cpp ├── error.cpp ├── texture.cpp ├── grid_layout_widget.hpp ├── alpha_texture.cpp ├── tab_widget.hpp ├── ordered_map_file.hpp ├── png_image.cpp ├── sunken_box.cpp ├── audio_file.hpp ├── midi_hardware.hpp ├── widget.cpp ├── event_dispatcher.hpp ├── genesis_editor.hpp ├── shader_program.cpp ├── audio_graph.hpp ├── dockable_pane_widget.hpp ├── locked_queue.hpp ├── midi_note_pitch.hpp ├── generate_unicode_data.cpp ├── crc32.cpp ├── uint256.hpp ├── gui.hpp ├── gui_window.hpp ├── byte_buffer.cpp ├── os.hpp ├── select_widget.cpp ├── shader_program_manager.cpp ├── genesis.hpp ├── track_editor_widget.hpp ├── settings_file.hpp ├── text_widget.hpp └── list.hpp ├── cmake ├── FindPNG.cmake ├── FindGLFW.cmake ├── Findrhash.cmake ├── FindLaxJson.cmake ├── Findepoxy.cmake ├── FindSoundIo.cmake ├── FindFreeType.cmake ├── Findrucksack.cmake └── Findffmpeg.cmake └── example ├── list_supported_formats.c ├── list_devices.c └── delay.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /test/tiny-sine.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/test/tiny-sine.ogg -------------------------------------------------------------------------------- /assets/img/null.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/null.png -------------------------------------------------------------------------------- /assets/img/play_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/play_head.png -------------------------------------------------------------------------------- /assets/font/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/font/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /assets/img/font-awesome/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/check.png -------------------------------------------------------------------------------- /assets/img/font-awesome/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/file.png -------------------------------------------------------------------------------- /assets/img/font-awesome/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/music.png -------------------------------------------------------------------------------- /assets/img/font-awesome/plug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/plug.png -------------------------------------------------------------------------------- /assets/img/font-awesome/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/folder.png -------------------------------------------------------------------------------- /assets/img/font-awesome/arrow-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/arrow-down.png -------------------------------------------------------------------------------- /assets/img/font-awesome/arrow-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/arrow-left.png -------------------------------------------------------------------------------- /assets/img/font-awesome/arrow-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/arrow-up.png -------------------------------------------------------------------------------- /assets/img/font-awesome/caret-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/caret-down.png -------------------------------------------------------------------------------- /assets/img/font-awesome/microphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/microphone.png -------------------------------------------------------------------------------- /assets/img/font-awesome/volume-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/volume-up.png -------------------------------------------------------------------------------- /assets/img/font-awesome/arrow-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/arrow-right.png -------------------------------------------------------------------------------- /assets/img/font-awesome/caret-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/caret-right.png -------------------------------------------------------------------------------- /assets/img/font-awesome/folder-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/folder-open.png -------------------------------------------------------------------------------- /assets/img/font-awesome/minus-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/minus-square.png -------------------------------------------------------------------------------- /assets/img/font-awesome/plus-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/plus-square.png -------------------------------------------------------------------------------- /assets/img/font-awesome/exclamation-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewrk/genesis/HEAD/assets/img/font-awesome/exclamation-circle.png -------------------------------------------------------------------------------- /src/glfw.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_HPP 2 | #define GLFW_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/freetype.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FREETYPE_HPP 2 | #define FREETYPE_HPP 3 | 4 | #include 5 | #include FT_FREETYPE_H 6 | #include FT_GLYPH_H 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/glm.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GLM_HPP 2 | #define GLM_HPP 3 | 4 | #define GLM_FORCE_RADIANS 5 | #include 6 | #include 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/synth.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SYNTH_HPP 2 | #define SYNTH_HPP 3 | 4 | #include "genesis.hpp" 5 | 6 | int create_synth_descriptor(GenesisPipeline *pipeline); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /test/thread_safe_queue_test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_SAFE_QUEUE_TEST_HPP 2 | #define THREAD_SAFE_QUEUE_TEST_HPP 3 | 4 | void test_thread_safe_queue(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/crc32.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CRC32_HPP 2 | #define CRC32_HPP 3 | 4 | #include 5 | 6 | uint32_t crc32(uint32_t crc, const unsigned char *buf, int len); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/resample.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RESAMPLE_HPP 2 | #define RESAMPLE_HPP 3 | 4 | #include "genesis.hpp" 5 | 6 | int create_resample_descriptor(GenesisPipeline *pipeline); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /test/ring_buffer_test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RING_BUFFER_TEST_HPP 2 | #define RING_BUFFER_TEST_HPP 3 | 4 | #undef NDEBUG 5 | 6 | void test_ring_buffer(void); 7 | 8 | #endif 9 | 10 | -------------------------------------------------------------------------------- /src/delay.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DELAY_HPP 2 | #define DELAY_HPP 3 | 4 | #include "genesis.hpp" 5 | 6 | int create_delay_descriptor(GenesisPipeline *pipeline); 7 | 8 | #endif 9 | 10 | -------------------------------------------------------------------------------- /test/ordered_map_file_test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ORDERED_MAP_FILE_TEST_HPP 2 | #define ORDERED_MAP_FILE_TEST_HPP 3 | 4 | #undef NDEBUG 5 | 6 | void test_ordered_map_file(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/warning.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WARNING_HPP 2 | #define WARNING_HPP 3 | 4 | enum Warning { 5 | WarningHighPriorityThread, 6 | 7 | WarningCount, 8 | }; 9 | 10 | void emit_warning(Warning warning); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/dragged_sample_file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DRAGGED_SAMPLE_FILE_HPP 2 | #define DRAGGED_SAMPLE_FILE_HPP 3 | 4 | #include "byte_buffer.hpp" 5 | 6 | struct DraggedSampleFile { 7 | ByteBuffer full_path; 8 | }; 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/mixer_node.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MIXER_NODE_HPP 2 | #define MIXER_NODE_HPP 3 | 4 | #include "genesis.hpp" 5 | 6 | int create_mixer_descriptor(GenesisPipeline *pipeline, int input_port_count, 7 | GenesisNodeDescriptor **out); 8 | 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /src/id_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ID_MAP_HPP 2 | #define ID_MAP_HPP 3 | 4 | #include "hash_map.hpp" 5 | #include "uint256.hpp" 6 | 7 | uint32_t hash_uint256(const uint256 & a); 8 | 9 | template 10 | using IdMap = HashMap; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/id_map.cpp: -------------------------------------------------------------------------------- 1 | #include "id_map.hpp" 2 | 3 | // this can't be inline static, or else any global variables that use it will 4 | // secretly become static, even if you declare them with extern. 5 | uint32_t hash_uint256(const uint256 & a) { 6 | return UIntOversized<4>::hash(a); 7 | } 8 | -------------------------------------------------------------------------------- /src/debug_gl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_GL_HPP 2 | #define DEBUG_GL_HPP 3 | 4 | #include "glfw.hpp" 5 | #include "util.hpp" 6 | 7 | static inline void assert_no_gl_error() { 8 | #ifdef NDEBUG 9 | #else 10 | GLenum err = glGetError(); 11 | if (err != GL_NO_ERROR) { 12 | panic("GL error: %d\n", (int) err); 13 | } 14 | #endif 15 | } 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/random.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_HPP 2 | #define RANDOM_HPP 3 | 4 | #include 5 | 6 | struct RandomState { 7 | static const int ARRAY_SIZE = 624; 8 | uint32_t array[ARRAY_SIZE]; 9 | int index; 10 | }; 11 | 12 | void init_random_state(RandomState * state, uint32_t seed); 13 | uint32_t get_random(RandomState * state); 14 | 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_CONFIG_H 2 | #define GENESIS_CONFIG_H 3 | 4 | #define GENESIS_VERSION_MAJOR @LIBGENESIS_VERSION_MAJOR@ 5 | #define GENESIS_VERSION_MINOR @LIBGENESIS_VERSION_MINOR@ 6 | #define GENESIS_VERSION_PATCH @LIBGENESIS_VERSION_PATCH@ 7 | #define GENESIS_VERSION_STRING "@LIBGENESIS_VERSION@" 8 | 9 | #cmakedefine GENESIS_HAVE_ALSA 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/resource_bundle.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RESOURCE_BUNDLE_HPP 2 | #define RESOURCE_BUNDLE_HPP 3 | 4 | #include "byte_buffer.hpp" 5 | 6 | #include 7 | 8 | class ResourceBundle { 9 | public: 10 | ResourceBundle(const char *filename); 11 | ~ResourceBundle(); 12 | 13 | void get_file_buffer(const char *key, ByteBuffer &buffer); 14 | 15 | RuckSackBundle *_bundle; 16 | }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "os.hpp" 2 | #include "genesis_editor.hpp" 3 | #include "error.h" 4 | 5 | int main(int argc, char *argv[]) { 6 | // If genesis depends on libgenesis then we need this code. 7 | int err; 8 | if ((err = os_init(nullptr))) 9 | panic("unable to initialize: %s", genesis_strerror(err)); 10 | 11 | GenesisEditor genesis_editor; 12 | genesis_editor.exec(); 13 | 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/ffmpeg.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_FFMPEG_HPP 2 | #define GENESIS_FFMPEG_HPP 3 | 4 | extern "C" { 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/sha_256_hasher.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHA_256_HASHER_HPP 2 | #define SHA_256_HASHER_HPP 3 | 4 | #include "byte_buffer.hpp" 5 | 6 | struct rhash_context; 7 | 8 | class Sha256Hasher { 9 | public: 10 | Sha256Hasher(); 11 | ~Sha256Hasher(); 12 | 13 | void update(char *buffer, size_t len); 14 | void get_digest(ByteBuffer &out); 15 | void reset(); 16 | 17 | rhash_context *rhc; 18 | bool needs_reset; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /cmake/FindPNG.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # PNG_FOUND 6 | # PNG_INCLUDE_DIR 7 | # PNG_LIBRARY 8 | 9 | find_path(PNG_INCLUDE_DIR NAMES png.h) 10 | 11 | find_library(PNG_LIBRARY NAMES png) 12 | 13 | include(FindPackageHandleStandardArgs) 14 | find_package_handle_standard_args(PNG DEFAULT_MSG PNG_LIBRARY PNG_INCLUDE_DIR) 15 | 16 | mark_as_advanced(PNG_INCLUDE_DIR PNG_LIBRARY) 17 | -------------------------------------------------------------------------------- /src/static_geometry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STATIC_GEOMETRY_HPP 2 | #define STATIC_GEOMETRY_HPP 3 | 4 | #include "glfw.hpp" 5 | 6 | class StaticGeometry { 7 | public: 8 | StaticGeometry(); 9 | ~StaticGeometry(); 10 | 11 | GLuint _rect_2d_vertex_buffer; 12 | GLuint _rect_2d_tex_coord_buffer; 13 | 14 | 15 | private: 16 | 17 | StaticGeometry(const StaticGeometry ©) = delete; 18 | StaticGeometry &operator=(const StaticGeometry ©) = delete; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /cmake/FindGLFW.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # GLFW_FOUND 6 | # GLFW_INCLUDE_DIR 7 | # GLFW_LIBRARY 8 | 9 | find_path(GLFW_INCLUDE_DIR NAMES GLFW/glfw3.h) 10 | 11 | find_library(GLFW_LIBRARY NAMES glfw glfw3) 12 | 13 | include(FindPackageHandleStandardArgs) 14 | find_package_handle_standard_args(GLFW DEFAULT_MSG GLFW_LIBRARY GLFW_INCLUDE_DIR) 15 | 16 | mark_as_advanced(GLFW_INCLUDE_DIR GLFW_LIBRARY) 17 | -------------------------------------------------------------------------------- /cmake/Findrhash.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # RHASH_FOUND 6 | # RHASH_INCLUDE_DIR 7 | # RHASH_LIBRARY 8 | 9 | find_path(RHASH_INCLUDE_DIR NAMES rhash.h) 10 | 11 | find_library(RHASH_LIBRARY NAMES rhash) 12 | 13 | include(FindPackageHandleStandardArgs) 14 | find_package_handle_standard_args(RHASH DEFAULT_MSG RHASH_LIBRARY RHASH_INCLUDE_DIR) 15 | 16 | mark_as_advanced(RHASH_INCLUDE_DIR RHASH_LIBRARY) 17 | -------------------------------------------------------------------------------- /cmake/FindLaxJson.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # LAXJSON_FOUND 6 | # LAXJSON_INCLUDE_DIR 7 | # LAXJSON_LIBRARY 8 | 9 | find_path(LAXJSON_INCLUDE_DIR laxjson.h) 10 | find_library(LAXJSON_LIBRARY NAMES laxjson) 11 | 12 | include(FindPackageHandleStandardArgs) 13 | find_package_handle_standard_args(LAXJSON DEFAULT_MSG LAXJSON_LIBRARY LAXJSON_INCLUDE_DIR) 14 | 15 | mark_as_advanced(LAXJSON_INCLUDE_DIR LAXJSON_LIBRARY) 16 | -------------------------------------------------------------------------------- /cmake/Findepoxy.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # EPOXY_FOUND 6 | # EPOXY_INCLUDE_DIR 7 | # EPOXY_LIBRARY 8 | 9 | find_path(EPOXY_INCLUDE_DIR NAMES epoxy/gl.h epoxy/glx.h) 10 | 11 | find_library(EPOXY_LIBRARY NAMES epoxy) 12 | 13 | include(FindPackageHandleStandardArgs) 14 | find_package_handle_standard_args(EPOXY DEFAULT_MSG EPOXY_LIBRARY EPOXY_INCLUDE_DIR) 15 | 16 | mark_as_advanced(EPOXY_INCLUDE_DIR EPOXY_LIBRARY) 17 | -------------------------------------------------------------------------------- /cmake/FindSoundIo.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # SOUNDIO_FOUND 6 | # SOUNDIO_INCLUDE_DIR 7 | # SOUNDIO_LIBRARY 8 | 9 | find_path(SOUNDIO_INCLUDE_DIR NAMES soundio/soundio.h) 10 | 11 | find_library(SOUNDIO_LIBRARY NAMES soundio) 12 | 13 | include(FindPackageHandleStandardArgs) 14 | find_package_handle_standard_args(SOUNDIO DEFAULT_MSG SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR) 15 | 16 | mark_as_advanced(SOUNDIO_INCLUDE_DIR SOUNDIO_LIBRARY) 17 | -------------------------------------------------------------------------------- /src/png_image.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PNG_IMAGE_HPP 2 | #define PNG_IMAGE_HPP 3 | 4 | #include "byte_buffer.hpp" 5 | 6 | class PngImage { 7 | public: 8 | // panics if the image could not be loaded 9 | PngImage(const ByteBuffer &compressed_bytes); 10 | ~PngImage() {} 11 | 12 | int _width; 13 | int _height; 14 | int _pitch; 15 | 16 | void gl_pixel_store_alignment() const; 17 | 18 | const char *raw() const { 19 | return _image_data.raw(); 20 | } 21 | 22 | private: 23 | ByteBuffer _image_data; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /cmake/FindFreeType.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # FREETYPE_FOUND 6 | # FREETYPE_INCLUDE_DIR 7 | # FREETYPE_LIBRARY 8 | 9 | find_path(FREETYPE_INCLUDE_DIR 10 | NAMES ft2build.h 11 | PATH_SUFFIXES freetype2) 12 | 13 | find_library(FREETYPE_LIBRARY NAMES freetype) 14 | 15 | include(FindPackageHandleStandardArgs) 16 | find_package_handle_standard_args(FREETYPE DEFAULT_MSG FREETYPE_LIBRARY FREETYPE_INCLUDE_DIR) 17 | 18 | mark_as_advanced(FREETYPE_INCLUDE_DIR FREETYPE_LIBRARY) 19 | -------------------------------------------------------------------------------- /src/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | files: { 3 | "font.ttf": { 4 | path: "assets/font/OpenSans-Regular.ttf", 5 | }, 6 | }, 7 | textures: { 8 | spritesheet: { 9 | maxWidth: 256, 10 | maxHeight: 256, 11 | globImages: [ 12 | { 13 | path: "assets/img/", 14 | glob: "*", 15 | prefix: "img/", 16 | anchor: "topleft", 17 | }, 18 | { 19 | path: "assets/img/font-awesome/", 20 | glob: "*", 21 | prefix: "font-awesome/", 22 | anchor: "topleft", 23 | }, 24 | ], 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/device_id.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_DEVICE_ID_HPP 2 | #define GENESIS_DEVICE_ID_HPP 3 | 4 | // modifying this structure affects project file backward compatibility 5 | // modifying this structure affects settings file backward compatibility 6 | // If you add to this structure, add corresponding entry in all_device_ids 7 | enum DeviceId { 8 | DeviceIdInvalid, 9 | 10 | DeviceIdMainOut, 11 | DeviceIdMainIn, 12 | }; 13 | 14 | const char *device_id_str(DeviceId device_id); 15 | 16 | DeviceId device_id_from_str(const char *str); 17 | 18 | int device_id_count(void); 19 | enum DeviceId device_id_at(int index); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/texture.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURE_HPP 2 | #define TEXTURE_HPP 3 | 4 | #include "byte_buffer.hpp" 5 | #include "glm.hpp" 6 | #include "glfw.hpp" 7 | 8 | class Gui; 9 | class GuiWindow; 10 | class Texture { 11 | public: 12 | Texture(Gui *gui); 13 | ~Texture(); 14 | 15 | void send_pixels(const ByteBuffer &pixels, int width, int height); 16 | 17 | void draw(GuiWindow *window, const glm::mat4 &mvp); 18 | 19 | Gui *_gui; 20 | int _width; 21 | int _height; 22 | 23 | private: 24 | GLuint _texture_id; 25 | 26 | Texture(const Texture ©) = delete; 27 | Texture &operator=(const Texture ©) = delete; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/drag_event.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DRAG_EVENT_HPP 2 | #define DRAG_EVENT_HPP 3 | 4 | #include "mouse_event.hpp" 5 | 6 | enum DragType { 7 | DragTypeViewTab, 8 | DragTypeSampleFile, 9 | DragTypeAudioAsset, 10 | DragTypeAudioClip, 11 | }; 12 | 13 | enum DragAction { 14 | DragActionMove, 15 | DragActionDrop, 16 | DragActionOut, 17 | }; 18 | 19 | struct DragData { 20 | DragType drag_type; 21 | void *ptr; 22 | void (*destruct)(DragData *drag_data); 23 | }; 24 | 25 | struct DragEvent { 26 | DragAction action; 27 | MouseEvent orig_event; 28 | MouseEvent mouse_event; 29 | const DragData *drag_data; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/spacer_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_SPACER_WIDGET_HPP 2 | #define GENESIS_SPACER_WIDGET_HPP 3 | 4 | class SpacerWidget : public Widget { 5 | public: 6 | SpacerWidget(GuiWindow *gui_window, bool horiz, bool vert) : Widget(gui_window) { 7 | this->horiz = horiz; 8 | this->vert = vert; 9 | } 10 | ~SpacerWidget() override {} 11 | 12 | void draw(const glm::mat4 &projection) override {} 13 | 14 | int max_width() const override { 15 | return horiz ? -1 : 0; 16 | } 17 | 18 | int max_height() const override { 19 | return vert ? -1 : 0; 20 | } 21 | 22 | bool horiz; 23 | bool vert; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/atomics.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_ATOMICS_HPP 2 | #define GENESIS_ATOMICS_HPP 3 | 4 | #include 5 | using std::atomic_flag; 6 | using std::atomic_int; 7 | using std::atomic_long; 8 | using std::atomic_bool; 9 | using std::atomic_uintptr_t; 10 | 11 | #if ATOMIC_INT_LOCK_FREE != 2 12 | #error "require atomic_int to be lock free" 13 | #endif 14 | 15 | #if ATOMIC_LONG_LOCK_FREE != 2 16 | #error "require atomic_long to be lock free" 17 | #endif 18 | 19 | #if ATOMIC_BOOL_LOCK_FREE != 2 20 | #error "require atomic_bool to be lock free" 21 | #endif 22 | 23 | #if ATOMIC_POINTER_LOCK_FREE != 2 24 | #error "require atomic pointers to be lock free" 25 | #endif 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/atomic_double.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ATOMIC_DOUBLE_HPP 2 | #define ATOMIC_DOUBLE_HPP 3 | 4 | #include "atomics.hpp" 5 | using std::atomic; 6 | 7 | class AtomicDouble { 8 | public: 9 | AtomicDouble() { } 10 | 11 | void add(double x) { 12 | double current_value = _value.load(); 13 | while (!_value.compare_exchange_weak(current_value, current_value + x)) {} 14 | } 15 | 16 | void store(double x) { 17 | _value.store(x); 18 | } 19 | 20 | double load() const { 21 | return _value.load(); 22 | } 23 | 24 | double exchange(double new_value) { 25 | return _value.exchange(new_value); 26 | } 27 | 28 | private: 29 | atomic _value; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/alpha_texture.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ALPHA_TEXTURE_HPP 2 | #define ALPHA_TEXTURE_HPP 3 | 4 | #include "byte_buffer.hpp" 5 | #include "glm.hpp" 6 | #include "glfw.hpp" 7 | 8 | class Gui; 9 | class GuiWindow; 10 | class AlphaTexture { 11 | public: 12 | AlphaTexture(Gui *gui); 13 | ~AlphaTexture(); 14 | 15 | void send_pixels(const ByteBuffer &pixels, int width, int height); 16 | 17 | void draw(GuiWindow *window, const glm::vec4 &color, const glm::mat4 &mvp); 18 | 19 | Gui *_gui; 20 | int _width; 21 | int _height; 22 | 23 | private: 24 | GLuint _texture_id; 25 | 26 | AlphaTexture(const AlphaTexture ©) = delete; 27 | AlphaTexture &operator=(const AlphaTexture ©) = delete; 28 | }; 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /cmake/Findrucksack.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # RUCKSACK_FOUND 6 | # RUCKSACK_INCLUDE_DIR 7 | # RUCKSACK_LIBRARY 8 | # RUCKSACKSPRITESHEET_LIBRARY 9 | # RUCKSACK_BINARY 10 | 11 | find_path(RUCKSACK_INCLUDE_DIR NAMES rucksack/rucksack.h) 12 | 13 | find_library(RUCKSACK_LIBRARY NAMES rucksack) 14 | find_library(RUCKSACK_LIBRARY NAMES rucksackspritesheet) 15 | 16 | find_program(RUCKSACK_BINARY rucksack) 17 | 18 | include(FindPackageHandleStandardArgs) 19 | find_package_handle_standard_args(RUCKSACK DEFAULT_MSG RUCKSACK_LIBRARY RUCKSACK_INCLUDE_DIR) 20 | 21 | mark_as_advanced(RUCKSACK_INCLUDE_DIR RUCKSACK_LIBRARY RUCKSACKSPRITESHEET_LIBRARY RUCKSACK_BINARY) 22 | -------------------------------------------------------------------------------- /src/warning.cpp: -------------------------------------------------------------------------------- 1 | #include "warning.hpp" 2 | #include "util.hpp" 3 | #include 4 | 5 | static bool seen_warnings[WarningCount] = {0}; 6 | 7 | void emit_warning(Warning warning) { 8 | if (seen_warnings[warning]) 9 | return; 10 | seen_warnings[warning] = true; 11 | 12 | switch (warning) { 13 | case WarningHighPriorityThread: 14 | fprintf(stderr, "warning: unable to set high priority thread: Operation not permitted\n"); 15 | fprintf(stderr, "See https://github.com/andrewrk/genesis/wiki/warning:-unable-to-set-high-priority-thread:-Operation-not-permitted\n"); 16 | return; 17 | case WarningCount: 18 | panic("invalid warning"); 19 | } 20 | panic("invalid warning"); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/mouse_event.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MOUSE_EVENT_HPP 2 | #define MOUSE_EVENT_HPP 3 | 4 | enum MouseButton { 5 | MouseButtonNone, 6 | MouseButtonLeft, 7 | MouseButtonMiddle, 8 | MouseButtonRight, 9 | }; 10 | 11 | enum MouseAction { 12 | MouseActionMove, 13 | MouseActionDown, 14 | MouseActionUp, 15 | }; 16 | 17 | struct MouseButtons { 18 | bool left; 19 | bool middle; 20 | bool right; 21 | }; 22 | 23 | struct MouseEvent { 24 | int x; 25 | int y; 26 | MouseButton button; 27 | MouseAction action; 28 | MouseButtons buttons; 29 | int modifiers; 30 | int dbl_click_count; 31 | }; 32 | 33 | struct MouseWheelEvent { 34 | int x; 35 | int y; 36 | int wheel_x; 37 | int wheel_y; 38 | int modifiers; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/render_job.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_RENDER_JOB 2 | #define GENESIS_RENDER_JOB 3 | 4 | struct Project; 5 | struct AudioGraph; 6 | struct GenesisContext; 7 | class Gui; 8 | 9 | struct RenderJob { 10 | Project *project; 11 | AudioGraph *audio_graph; 12 | GenesisContext *genesis_context; 13 | Gui *gui; 14 | bool is_complete; 15 | }; 16 | 17 | void render_job_init(RenderJob *rj, Project *project, GenesisContext *genesis_context, Gui *gui); 18 | void render_job_deinit(RenderJob *rj); 19 | 20 | void render_job_start(RenderJob *rj, const GenesisExportFormat *export_format, const ByteBuffer &out_path); 21 | float render_job_progress(RenderJob *rj); 22 | 23 | void render_job_stop(RenderJob *rj); 24 | bool render_job_is_complete(RenderJob *rj); 25 | 26 | void render_job_flush_events(RenderJob *rj); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/sha_256_hasher.cpp: -------------------------------------------------------------------------------- 1 | #include "sha_256_hasher.hpp" 2 | #include "util.hpp" 3 | 4 | #include 5 | 6 | Sha256Hasher::Sha256Hasher() { 7 | rhash_library_init(); 8 | rhc = ok_mem(rhash_init(RHASH_SHA256)); 9 | needs_reset = false; 10 | } 11 | 12 | Sha256Hasher::~Sha256Hasher() { 13 | rhash_free(rhc); 14 | } 15 | 16 | void Sha256Hasher::update(char *buffer, size_t len) { 17 | assert(!needs_reset); 18 | rhash_update(rhc, buffer, len); 19 | } 20 | 21 | void Sha256Hasher::get_digest(ByteBuffer &out) { 22 | assert(!needs_reset); 23 | rhash_final(rhc, nullptr); 24 | int digest_size = rhash_get_digest_size(RHASH_SHA256); 25 | out.resize(digest_size); 26 | rhash_print(out.raw(), rhc, RHASH_SHA256, RHPR_RAW); 27 | needs_reset = true; 28 | } 29 | 30 | void Sha256Hasher::reset() { 31 | rhash_reset(rhc); 32 | needs_reset = false; 33 | } 34 | -------------------------------------------------------------------------------- /src/project_props_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROJECT_PROPS_WIDGET_HPP 2 | #define PROJECT_PROPS_WIDGET_HPP 3 | 4 | #include "widget.hpp" 5 | #include "grid_layout_widget.hpp" 6 | 7 | struct Project; 8 | class TextWidget; 9 | class SelectWidget; 10 | 11 | class ProjectPropsWidget : public Widget { 12 | public: 13 | ProjectPropsWidget(GuiWindow *gui_window, Project *project); 14 | ~ProjectPropsWidget() override; 15 | void draw(const glm::mat4 &projection) override; 16 | void on_resize() override; 17 | 18 | void on_mouse_move(const MouseEvent *) override; 19 | 20 | 21 | Project *project; 22 | GridLayoutWidget layout; 23 | 24 | TextWidget *create_form_label(const char *text); 25 | 26 | SelectWidget *sample_rate_select; 27 | SelectWidget *channel_layout_select; 28 | void select_project_sample_rate(); 29 | void select_project_channel_layout(); 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/font_size.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FONT_SIZE_HPP 2 | #define FONT_SIZE_HPP 3 | 4 | #include "hash_map.hpp" 5 | #include "freetype.hpp" 6 | 7 | #include 8 | 9 | uint32_t hash_uint32_t(const uint32_t &); 10 | 11 | struct FontCacheValue { 12 | FT_Glyph glyph; 13 | FT_BitmapGlyph bitmap_glyph; 14 | FT_UInt glyph_index; 15 | int above_size; 16 | int below_size; 17 | }; 18 | 19 | class FontSize { 20 | public: 21 | FontSize(FT_Face font_face, int font_size); 22 | ~FontSize(); 23 | 24 | FontCacheValue font_cache_entry(uint32_t codepoint); 25 | 26 | int _max_above_size; 27 | int _max_below_size; 28 | 29 | private: 30 | HashMap _font_cache; 31 | 32 | FT_Face _font_face; 33 | int _font_size; 34 | 35 | FontSize &operator=(const FontSize&) = delete; 36 | FontSize(const FontSize&) = delete; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/resource_bundle.cpp: -------------------------------------------------------------------------------- 1 | #include "resource_bundle.hpp" 2 | 3 | ResourceBundle::ResourceBundle(const char *filename) { 4 | int err = rucksack_bundle_open_read(filename, &_bundle); 5 | if (err) 6 | panic("Unable to open resource bundle: %s", rucksack_err_str(err)); 7 | } 8 | 9 | ResourceBundle::~ResourceBundle() { 10 | rucksack_bundle_close(_bundle); 11 | } 12 | 13 | void ResourceBundle::get_file_buffer(const char *key, ByteBuffer &buffer) { 14 | RuckSackFileEntry *entry = rucksack_bundle_find_file(_bundle, key, -1); 15 | 16 | if (!entry) 17 | panic("could not find %s in resource bundle", key); 18 | 19 | long size = rucksack_file_size(entry); 20 | 21 | buffer.resize(size); 22 | 23 | int err = rucksack_file_read(entry, (unsigned char*)buffer.raw()); 24 | 25 | if (err) 26 | panic("error reading '%s' resource: %s", key, rucksack_err_str(err)); 27 | } 28 | -------------------------------------------------------------------------------- /src/static_geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "static_geometry.hpp" 2 | 3 | StaticGeometry::StaticGeometry() { 4 | GLfloat rect_2d_vertexes[4][3] = { 5 | {0, 0, 0}, 6 | {0, 1.0f, 0}, 7 | {1.0f, 0, 0}, 8 | {1.0f, 1.0f, 0}, 9 | }; 10 | glGenBuffers(1, &_rect_2d_vertex_buffer); 11 | glBindBuffer(GL_ARRAY_BUFFER, _rect_2d_vertex_buffer); 12 | glBufferData(GL_ARRAY_BUFFER, 4 * 3 * sizeof(GLfloat), rect_2d_vertexes, GL_STATIC_DRAW); 13 | 14 | 15 | GLfloat rect_2d_tex_coords[4][2] = { 16 | {0, 0}, 17 | {0, 1}, 18 | {1, 0}, 19 | {1, 1}, 20 | }; 21 | glGenBuffers(1, &_rect_2d_tex_coord_buffer); 22 | glBindBuffer(GL_ARRAY_BUFFER, _rect_2d_tex_coord_buffer); 23 | glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), rect_2d_tex_coords, GL_STATIC_DRAW); 24 | 25 | } 26 | 27 | StaticGeometry::~StaticGeometry() { 28 | glDeleteBuffers(1, &_rect_2d_tex_coord_buffer); 29 | glDeleteBuffers(1, &_rect_2d_vertex_buffer); 30 | } 31 | -------------------------------------------------------------------------------- /src/sunken_box.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SUNKEN_BOX 2 | #define SUNKEN_BOX 3 | 4 | #include "widget.hpp" 5 | 6 | enum SunkenBoxScheme { 7 | SunkenBoxSchemeSunken, 8 | SunkenBoxSchemeRaised, 9 | SunkenBoxSchemeInactive, 10 | SunkenBoxSchemeSunkenBorders, 11 | SunkenBoxSchemeRaisedBorders, 12 | }; 13 | 14 | class SunkenBox { 15 | public: 16 | SunkenBox(); 17 | ~SunkenBox() {} 18 | 19 | void set_scheme(SunkenBoxScheme color_scheme); 20 | void update(Widget *widget, int left, int top, int width, int height); 21 | void draw(GuiWindow *gui_window, const glm::mat4 &projection); 22 | 23 | glm::vec4 top_color; 24 | glm::vec4 bottom_color; 25 | glm::mat4 gradient_model; 26 | glm::mat4 border_top_model; 27 | glm::mat4 border_right_model; 28 | glm::mat4 border_bottom_model; 29 | glm::mat4 border_left_model; 30 | bool borders_on; 31 | glm::vec4 border_top_color; 32 | glm::vec4 border_right_color; 33 | glm::vec4 border_bottom_color; 34 | glm::vec4 border_left_color; 35 | }; 36 | 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/device_id.cpp: -------------------------------------------------------------------------------- 1 | #include "device_id.hpp" 2 | #include "util.hpp" 3 | #include 4 | 5 | static const DeviceId all_device_ids[] = { 6 | DeviceIdMainOut, 7 | DeviceIdMainIn, 8 | }; 9 | 10 | const char *device_id_str(DeviceId device_id) { 11 | switch (device_id) { 12 | case DeviceIdInvalid: 13 | panic("invalid device id"); 14 | case DeviceIdMainOut: 15 | return "Main Out"; 16 | case DeviceIdMainIn: 17 | return "Main In"; 18 | } 19 | panic("invalid device id"); 20 | } 21 | 22 | int device_id_count(void) { 23 | return array_length(all_device_ids); 24 | } 25 | 26 | DeviceId device_id_at(int index) { 27 | assert(index < array_length(all_device_ids)); 28 | return all_device_ids[index]; 29 | } 30 | 31 | DeviceId device_id_from_str(const char *str) { 32 | for (int i = 0; i < array_length(all_device_ids); i += 1) { 33 | const char *this_str = device_id_str(all_device_ids[i]); 34 | if (strcmp(this_str, str) == 0) { 35 | return all_device_ids[i]; 36 | } 37 | } 38 | return DeviceIdInvalid; 39 | } 40 | -------------------------------------------------------------------------------- /src/spritesheet.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SPRITESHEET_HPP 2 | #define SPRITESHEET_HPP 3 | 4 | #include "shader_program.hpp" 5 | #include "byte_buffer.hpp" 6 | #include "hash_map.hpp" 7 | #include "glfw.hpp" 8 | 9 | class Spritesheet; 10 | struct SpritesheetImage { 11 | int x; 12 | int y; 13 | int width; 14 | int height; 15 | float anchor_x; 16 | float anchor_y; 17 | bool r90; 18 | GLuint vertex_buffer; 19 | GLuint tex_coord_buffer; 20 | Spritesheet *spritesheet; 21 | }; 22 | 23 | class Gui; 24 | class GuiWindow; 25 | class Spritesheet { 26 | public: 27 | Spritesheet(Gui *gui, const ByteBuffer &key); 28 | ~Spritesheet(); 29 | 30 | const SpritesheetImage *get_image_info(const ByteBuffer &key) const; 31 | void draw(GuiWindow *window, const SpritesheetImage *image, const glm::mat4 &mvp) const; 32 | void draw_color(GuiWindow *window, const SpritesheetImage *image, 33 | const glm::mat4 &mvp, const glm::vec4 &color) const; 34 | 35 | private: 36 | Gui *_gui; 37 | GLuint _texture_id; 38 | 39 | HashMap _info_dict; 40 | 41 | Spritesheet(const Spritesheet ©) = delete; 42 | Spritesheet &operator=(const Spritesheet ©) = delete; 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/sequencer_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_SEQUENCER_WIDGET_HPP 2 | #define GENESIS_SEQUENCER_WIDGET_HPP 3 | 4 | #include "widget.hpp" 5 | #include "sunken_box.hpp" 6 | 7 | class GuiWindow; 8 | struct Project; 9 | class ScrollBarWidget; 10 | class Label; 11 | 12 | struct SequencerWidgetGridRow { 13 | Label *label; 14 | glm::mat4 label_model; 15 | glm::mat4 model; 16 | }; 17 | 18 | class SequencerWidget : public Widget { 19 | public: 20 | SequencerWidget(GuiWindow *window, Project *project); 21 | ~SequencerWidget() override; 22 | 23 | void draw(const glm::mat4 &projection) override; 24 | void on_resize() override { update_model(); } 25 | void on_mouse_move(const MouseEvent *event) override; 26 | void on_mouse_wheel(const MouseWheelEvent *event) override; 27 | 28 | Project *project; 29 | SunkenBox bg; 30 | SunkenBox grid_bg; 31 | ScrollBarWidget *vert_scroll_bar; 32 | ScrollBarWidget *horiz_scroll_bar; 33 | 34 | glm::vec4 grid_row_color; 35 | glm::vec4 note_label_color; 36 | 37 | List grid_rows; 38 | 39 | void update_model(); 40 | 41 | void deinit_grid_row(SequencerWidgetGridRow *grid_row); 42 | void get_note_index_text(int note_index, String &text); 43 | }; 44 | 45 | #endif 46 | 47 | -------------------------------------------------------------------------------- /src/ring_buffer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_RING_BUFFER_HPP 2 | #define GENESIS_RING_BUFFER_HPP 3 | 4 | #include "util.hpp" 5 | #include "atomics.hpp" 6 | #include "os.hpp" 7 | 8 | struct RingBuffer { 9 | OsMirroredMemory mem; 10 | atomic_long write_offset; 11 | atomic_long read_offset; 12 | int capacity; 13 | }; 14 | 15 | int ring_buffer_init(struct RingBuffer *rb, int requested_capacity); 16 | void ring_buffer_deinit(struct RingBuffer *rb); 17 | 18 | /// Do not write more than capacity. 19 | char *ring_buffer_write_ptr(struct RingBuffer *ring_buffer); 20 | /// `count` in bytes. 21 | void ring_buffer_advance_write_ptr(struct RingBuffer *ring_buffer, int count); 22 | 23 | /// Do not read more than capacity. 24 | char *ring_buffer_read_ptr(struct RingBuffer *ring_buffer); 25 | /// `count` in bytes. 26 | void ring_buffer_advance_read_ptr(struct RingBuffer *ring_buffer, int count); 27 | 28 | /// Returns how many bytes of the buffer is used, ready for reading. 29 | int ring_buffer_fill_count(struct RingBuffer *ring_buffer); 30 | 31 | /// Returns how many bytes of the buffer is free, ready for writing. 32 | int ring_buffer_free_count(struct RingBuffer *ring_buffer); 33 | 34 | /// Must be called by the writer. 35 | void ring_buffer_clear(struct RingBuffer *ring_buffer); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/button_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_BUTTON_WIDGET_HPP 2 | #define GENESIS_BUTTON_WIDGET_HPP 3 | 4 | #include "string.hpp" 5 | #include "label.hpp" 6 | #include "widget.hpp" 7 | #include "sunken_box.hpp" 8 | #include "event_dispatcher.hpp" 9 | 10 | struct SpritesheetImage; 11 | 12 | class ButtonWidget : public Widget { 13 | public: 14 | ButtonWidget(GuiWindow *gui_window); 15 | ~ButtonWidget() override; 16 | 17 | void draw(const glm::mat4 &projection) override; 18 | 19 | void on_mouse_move(const MouseEvent *event) override; 20 | void on_mouse_out(const MouseEvent *event) override; 21 | void on_mouse_over(const MouseEvent *event) override; 22 | void on_resize() override { update_model(); } 23 | 24 | int min_width() const override; 25 | int max_width() const override; 26 | int min_height() const override; 27 | int max_height() const override; 28 | 29 | void set_text(const String &text); 30 | 31 | EventDispatcher events; 32 | 33 | Label label; 34 | glm::mat4 label_model; 35 | SunkenBox bg; 36 | 37 | int padding_left; 38 | int padding_right; 39 | int padding_top; 40 | int padding_bottom; 41 | glm::vec4 text_color; 42 | 43 | bool hovering; 44 | bool mouse_down; 45 | 46 | void update_model(); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/mixer_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MIXER_WIDGET_HPP 2 | #define MIXER_WIDGET_HPP 3 | 4 | #include "widget.hpp" 5 | #include "project.hpp" 6 | #include "sunken_box.hpp" 7 | 8 | class GuiWindow; 9 | class Label; 10 | struct Project; 11 | 12 | struct GuiMixerLine { 13 | MixerLine *mixer_line; 14 | SunkenBox bg; 15 | Label *name_label; 16 | glm::mat4 name_label_model; 17 | }; 18 | 19 | struct GuiEffect { 20 | Effect *effect; 21 | Label *name_label; 22 | glm::mat4 name_label_model; 23 | SunkenBox bg; 24 | }; 25 | 26 | class MixerWidget : public Widget { 27 | public: 28 | MixerWidget(GuiWindow *window, Project *project); 29 | ~MixerWidget() override; 30 | void draw(const glm::mat4 &projection) override; 31 | void on_resize() override { update_model(); } 32 | 33 | Project *project; 34 | 35 | void update_model(); 36 | void refresh_lines(); 37 | 38 | private: 39 | glm::vec4 line_name_color; 40 | 41 | List gui_lines; 42 | List gui_effects; 43 | 44 | SunkenBox fx_area_bg; 45 | 46 | GuiMixerLine * create_gui_mixer_line(); 47 | void destroy_gui_mixer_line(GuiMixerLine *gui_mixer_line); 48 | 49 | GuiEffect *create_gui_effect(); 50 | void destroy_gui_effect(GuiEffect *gui_effect); 51 | 52 | void refresh_fx_list(); 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/shader_program_manager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_PROGRAM_MANAGER 2 | #define SHADER_PROGRAM_MANAGER 3 | 4 | #include "shader_program.hpp" 5 | 6 | class ShaderProgramManager { 7 | public: 8 | ShaderProgramManager(); 9 | ~ShaderProgramManager(); 10 | 11 | ShaderProgramManager(const ShaderProgramManager ©) = delete; 12 | ShaderProgramManager &operator=(const ShaderProgramManager ©) = delete; 13 | 14 | ShaderProgram _texture_shader_program; 15 | GLint _texture_attrib_tex_coord; 16 | GLint _texture_attrib_position; 17 | GLint _texture_uniform_mvp; 18 | GLint _texture_uniform_tex; 19 | 20 | ShaderProgram _text_shader_program; 21 | GLint _text_attrib_tex_coord; 22 | GLint _text_attrib_position; 23 | GLint _text_uniform_mvp; 24 | GLint _text_uniform_tex; 25 | GLint _text_uniform_color; 26 | 27 | ShaderProgram _primitive_shader_program; 28 | GLint _primitive_attrib_position; 29 | GLint _primitive_uniform_mvp; 30 | GLint _primitive_uniform_color; 31 | 32 | ShaderProgram texture_color_program; 33 | GLint texture_color_attrib_tex_coord; 34 | GLint texture_color_attrib_position; 35 | GLint texture_color_uniform_mvp; 36 | GLint texture_color_uniform_tex; 37 | GLint texture_color_uniform_color; 38 | 39 | ShaderProgram gradient_program; 40 | GLint gradient_attrib_position; 41 | GLint gradient_uniform_mvp; 42 | GLint gradient_uniform_color_top; 43 | GLint gradient_uniform_color_bottom; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/sort_key.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SORT_KEY_HPP 2 | #define SORT_KEY_HPP 3 | 4 | #include "list.hpp" 5 | #include "byte_buffer.hpp" 6 | 7 | class SortKey { 8 | public: 9 | SortKey& operator= (const SortKey& other); 10 | SortKey(const SortKey &other); 11 | ~SortKey() { } 12 | 13 | static SortKey single(const SortKey *low, const SortKey *high); 14 | static void multi(List &out_sort_key_list, const SortKey *low, const SortKey *high, int count); 15 | static int compare(const SortKey &a, const SortKey &b); 16 | 17 | int allocated_size() const { 18 | return digits.allocated_size(); 19 | } 20 | 21 | void serialize(ByteBuffer &buf) const; 22 | int deserialize(const ByteBuffer &buf, int *offset); 23 | 24 | // don't use these 25 | SortKey(); 26 | SortKey(int value); 27 | private: 28 | 29 | int magnitude; 30 | List digits; 31 | 32 | void normalize(); 33 | static SortKey average(const SortKey &low, const SortKey &high); 34 | static SortKey increment(const SortKey &value); 35 | static SortKey add(const SortKey &a, const SortKey &b); 36 | 37 | static void pad_to_equal_magnitude(SortKey &a, SortKey &b); 38 | static void pad_in_place(SortKey &sort_key, int magnitude); 39 | static void truncate_fraction(SortKey &value); 40 | static void multi_recurse(List &out_sort_key_list, 41 | const SortKey *low_value, const SortKey *high_value, 42 | int low_index, int high_index); 43 | }; 44 | 45 | #endif 46 | 47 | -------------------------------------------------------------------------------- /src/random.cpp: -------------------------------------------------------------------------------- 1 | #include "random.hpp" 2 | 3 | // adapted from http://en.wikipedia.org/wiki/Mersenne_twister#Pseudocode 4 | 5 | // Initialize the generator from a seed 6 | void init_random_state(RandomState * state, uint32_t seed) { 7 | state->index = 0; 8 | state->array[0] = seed; 9 | for (int i = 1; i < RandomState::ARRAY_SIZE; i++) { 10 | uint64_t previous_value = state->array[i - 1]; 11 | state->array[i] = ((previous_value ^ (previous_value << 30)) * 0x6c078965UL + i) & 0x00000000ffffffffULL; 12 | } 13 | } 14 | 15 | // fill the array with untempered numbers 16 | static void generate_numbers(RandomState * state) { 17 | for (int i = 0; i < RandomState::ARRAY_SIZE; i++) { 18 | uint32_t y = (state->array[i] & 0x80000000UL) + (state->array[(i + 1) % RandomState::ARRAY_SIZE] & 0x7fffffffUL); 19 | uint32_t untempered = state->array[(i + 397) % RandomState::ARRAY_SIZE] ^ (y >> 1); 20 | if ((y % 2) != 0) { 21 | // y is odd 22 | untempered = untempered ^ 0x9908b0dfUL; 23 | } 24 | state->array[i] = untempered; 25 | } 26 | } 27 | 28 | uint32_t get_random(RandomState * state) { 29 | if (state->index == 0) { 30 | generate_numbers(state); 31 | } 32 | 33 | // temper the number 34 | uint32_t y = state->array[state->index]; 35 | y ^= y >> 11; 36 | y ^= (y >> 7) & 0x9d2c5680UL; 37 | y ^= (y >> 15) & 0xefc60000UL; 38 | y ^= y >> 18; 39 | 40 | state->index = (state->index + 1) % RandomState::ARRAY_SIZE; 41 | return y; 42 | } 43 | -------------------------------------------------------------------------------- /src/shader_program.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_PROGRAM_HPP 2 | #define SHADER_PROGRAM_HPP 3 | 4 | #include "util.hpp" 5 | #include "glm.hpp" 6 | #include "glfw.hpp" 7 | 8 | class ShaderProgram { 9 | public: 10 | ShaderProgram( 11 | const char *vertex_shader_source, 12 | const char *fragment_shader_source, 13 | const char *geometry_shader_source); 14 | ~ShaderProgram(); 15 | 16 | void bind() const { 17 | glUseProgram(program_id); 18 | } 19 | 20 | GLint attrib_location(const char *name) { 21 | GLint id = glGetAttribLocation(program_id, name); 22 | if (id == -1) 23 | panic("invalid attrib: %s", name); 24 | return id; 25 | } 26 | 27 | GLint uniform_location(const char *name) { 28 | GLint id = glGetUniformLocation(program_id, name); 29 | if (id == -1) 30 | panic("invalid uniform: %s", name); 31 | return id; 32 | } 33 | 34 | void set_uniform(GLint uniformId, int value) const; 35 | void set_uniform(GLint uniformId, float value) const; 36 | void set_uniform(GLint uniformId, const glm::vec3 &value) const; 37 | void set_uniform(GLint uniformId, const glm::vec4 &value) const; 38 | void set_uniform(GLint uniformId, const glm::mat4 &value) const; 39 | void set_uniform(GLint uniformId, const glm::mat3 &value) const; 40 | 41 | private: 42 | 43 | GLuint program_id; 44 | GLuint vertex_id; 45 | GLuint fragment_id; 46 | 47 | GLuint geometry_id; 48 | bool have_geometry; 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/atomic_value.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ATOMIC_VALUE_HPP 2 | #define ATOMIC_VALUE_HPP 3 | 4 | #include "atomics.hpp" 5 | 6 | // single reader, single writer atomic value 7 | 8 | template 9 | class AtomicValue { 10 | public: 11 | AtomicValue() { 12 | _in_use_index = 0; 13 | _active_index = 0; 14 | _write_index = -1; 15 | } 16 | ~AtomicValue() {} 17 | 18 | T *write_begin() { 19 | assert(_write_index == -1); 20 | int in_use_index = _in_use_index.load(); 21 | int active_index = _active_index.load(); 22 | if (in_use_index != 0 && active_index != 0) 23 | _write_index = 0; 24 | else if (in_use_index != 1 && active_index != 1) 25 | _write_index = 1; 26 | else 27 | _write_index = 2; 28 | return &_values[_write_index]; 29 | } 30 | 31 | void write_end() { 32 | assert(_write_index != -1); 33 | _active_index.store(_write_index); 34 | _write_index = -1; 35 | } 36 | 37 | T *get_read_ptr() { 38 | _in_use_index.store(_active_index.load()); 39 | return &_values[_in_use_index]; 40 | } 41 | 42 | T *write(const T &value) { 43 | T *ptr = write_begin(); 44 | *ptr = value; 45 | write_end(); 46 | return ptr; 47 | } 48 | 49 | private: 50 | T _values[3]; 51 | atomic_int _in_use_index; 52 | atomic_int _active_index; 53 | int _write_index; 54 | 55 | AtomicValue(const AtomicValue ©) = delete; 56 | AtomicValue &operator=(const AtomicValue ©) = delete; 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/scroll_bar_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SCROLL_BAR_WIDGET 2 | #define SCROLL_BAR_WIDGET 3 | 4 | #include "widget.hpp" 5 | #include "event_dispatcher.hpp" 6 | #include "sunken_box.hpp" 7 | 8 | enum ScrollBarLayout { 9 | ScrollBarLayoutVert, 10 | ScrollBarLayoutHoriz, 11 | }; 12 | 13 | class GuiWindow; 14 | 15 | class ScrollBarWidget : public Widget { 16 | public: 17 | ScrollBarWidget(GuiWindow *gui_window, ScrollBarLayout layout); 18 | ~ScrollBarWidget() override; 19 | 20 | void draw(const glm::mat4 &projection) override; 21 | void on_mouse_move(const MouseEvent *) override; 22 | void on_mouse_wheel(const MouseWheelEvent *) override; 23 | 24 | void set_value(int value); 25 | void set_handle_ratio(int container_size, int content_size); 26 | 27 | int min_width() const override; 28 | int max_width() const override; 29 | int min_height() const override; 30 | int max_height() const override; 31 | 32 | void on_resize() override { update_model(); } 33 | 34 | ScrollBarLayout layout; 35 | float handle_ratio; 36 | int min_value; 37 | int max_value; 38 | int value; 39 | EventDispatcher events; 40 | SunkenBox bg; 41 | SunkenBox handle; 42 | int handle_left; 43 | int handle_right; 44 | int handle_top; 45 | int handle_bottom; 46 | int handle_track_long_size; 47 | int handle_long_size; 48 | int drag_start_x; 49 | int drag_start_y; 50 | bool dragging_handle; 51 | int drag_start_value; 52 | 53 | void update_model(); 54 | int long_dimension() const; 55 | float get_pos_amt(int x, int y); 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void panic(const char *format, ...) { 8 | va_list ap; 9 | va_start(ap, format); 10 | vfprintf(stderr, format, ap); 11 | fprintf(stderr, "\n"); 12 | va_end(ap); 13 | abort(); 14 | } 15 | 16 | char * create_formatted_str(const char *format, ...) { 17 | va_list ap, ap2; 18 | va_start(ap, format); 19 | va_copy(ap2, ap); 20 | 21 | int ret = vsnprintf(NULL, 0, format, ap); 22 | if (ret < 0) 23 | return nullptr; 24 | 25 | int required_length = ret + 1; 26 | char *result = allocate_zero(required_length); 27 | 28 | ret = vsnprintf(result, required_length, format, ap2); 29 | if (ret < 0) { 30 | destroy(result, required_length); 31 | return nullptr; 32 | } 33 | 34 | va_end(ap2); 35 | va_end(ap); 36 | 37 | result[required_length - 1] = 0; 38 | 39 | return result; 40 | } 41 | 42 | unsigned int greatest_common_denominator(unsigned int u, unsigned int v) { 43 | // The binary GCD algorithm. 44 | 45 | if (u == 0) 46 | return v; 47 | 48 | if (v == 0) 49 | return u; 50 | 51 | int shift; 52 | for (shift = 0; ((u | v) & 1) == 0; ++shift) { 53 | u >>= 1; 54 | v >>= 1; 55 | } 56 | 57 | while ((u & 1) == 0) 58 | u >>= 1; 59 | 60 | do { 61 | while ((v & 1) == 0) 62 | v >>= 1; 63 | 64 | if (u > v) { 65 | unsigned int tmp = v; 66 | v = u; 67 | u = tmp; 68 | } 69 | v -= u; 70 | } while (v != 0); 71 | 72 | return u << shift; 73 | } 74 | -------------------------------------------------------------------------------- /src/ring_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "ring_buffer.hpp" 2 | 3 | 4 | int ring_buffer_init(struct RingBuffer *rb, int requested_capacity) { 5 | int err; 6 | if ((err = os_init_mirrored_memory(&rb->mem, requested_capacity))) 7 | return err; 8 | rb->write_offset = 0; 9 | rb->read_offset = 0; 10 | rb->capacity = rb->mem.capacity; 11 | 12 | return 0; 13 | } 14 | 15 | void ring_buffer_deinit(struct RingBuffer *rb) { 16 | os_deinit_mirrored_memory(&rb->mem); 17 | } 18 | 19 | char *ring_buffer_write_ptr(struct RingBuffer *rb) { 20 | return rb->mem.address + (rb->write_offset % rb->capacity); 21 | } 22 | 23 | void ring_buffer_advance_write_ptr(struct RingBuffer *rb, int count) { 24 | rb->write_offset.fetch_add(count); 25 | assert(ring_buffer_fill_count(rb) >= 0); 26 | } 27 | 28 | char *ring_buffer_read_ptr(struct RingBuffer *rb) { 29 | return rb->mem.address + (rb->read_offset % rb->capacity); 30 | } 31 | 32 | void ring_buffer_advance_read_ptr(struct RingBuffer *rb, int count) { 33 | rb->read_offset.fetch_add(count); 34 | assert(ring_buffer_fill_count(rb) >= 0); 35 | } 36 | 37 | int ring_buffer_fill_count(struct RingBuffer *rb) { 38 | // Whichever offset we load first might have a smaller value. So we load 39 | // the read_offset first. 40 | long read_offset = rb->read_offset.load(); 41 | long write_offset = rb->write_offset.load(); 42 | int count = write_offset - read_offset; 43 | assert(count >= 0); 44 | assert(count <= rb->capacity); 45 | return count; 46 | } 47 | 48 | int ring_buffer_free_count(struct RingBuffer *rb) { 49 | return rb->capacity - ring_buffer_fill_count(rb); 50 | } 51 | 52 | void ring_buffer_clear(struct RingBuffer *rb) { 53 | return rb->write_offset.store(rb->read_offset.load()); 54 | } 55 | -------------------------------------------------------------------------------- /src/select_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_SELECT_WIDGET_HPP 2 | #define GENESIS_SELECT_WIDGET_HPP 3 | 4 | #include "string.hpp" 5 | #include "label.hpp" 6 | #include "widget.hpp" 7 | #include "sunken_box.hpp" 8 | #include "event_dispatcher.hpp" 9 | 10 | struct SpritesheetImage; 11 | class MenuWidgetItem; 12 | class SelectWidget; 13 | 14 | struct SelectWidgetItem { 15 | SelectWidget *parent; 16 | MenuWidgetItem *menu_item; 17 | String name; 18 | int index; 19 | }; 20 | 21 | class SelectWidget : public Widget { 22 | public: 23 | SelectWidget(GuiWindow *gui_window); 24 | ~SelectWidget() override; 25 | 26 | void draw(const glm::mat4 &projection) override; 27 | 28 | void on_mouse_move(const MouseEvent *event) override; 29 | void on_mouse_out(const MouseEvent *event) override; 30 | void on_mouse_over(const MouseEvent *event) override; 31 | void on_resize() override { update_model(); } 32 | 33 | int min_width() const override; 34 | int min_height() const override; 35 | int max_height() const override; 36 | 37 | void clear(); 38 | void append_choice(const String &choice); 39 | void select_index(int index); 40 | 41 | int selected_index; 42 | 43 | EventDispatcher events; 44 | 45 | Label label; 46 | glm::mat4 label_model; 47 | SunkenBox bg; 48 | MenuWidgetItem *context_menu; 49 | 50 | int padding_left; 51 | int padding_right; 52 | int padding_top; 53 | int padding_bottom; 54 | int icon_spacing; 55 | glm::vec4 text_color; 56 | 57 | const SpritesheetImage *arrow_icon_img; 58 | glm::mat4 arrow_icon_model; 59 | 60 | List items; 61 | 62 | bool hovering; 63 | bool context_menu_open; 64 | 65 | void update_model(); 66 | void set_activate_handlers(); 67 | void clear_context_menu(); 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /example/list_supported_formats.c: -------------------------------------------------------------------------------- 1 | #include "genesis.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // print the list of supported input and export formats, codecs, sample formats, and sample rates 8 | 9 | int main(int argc, char **argv) { 10 | fprintf(stderr, "libgenesis version %s\n", genesis_version_string()); 11 | 12 | struct GenesisContext *context; 13 | int err = genesis_context_create(&context); 14 | if (err) { 15 | fprintf(stderr, "unable to create genesis context: %s\n", genesis_strerror(err)); 16 | return 1; 17 | } 18 | fprintf(stdout, "\nImport:\n"); 19 | int in_format_count = genesis_in_format_count(context); 20 | for (int format_index = 0; format_index < in_format_count; format_index += 1) { 21 | struct GenesisAudioFileFormat *format = genesis_in_format_index(context, format_index); 22 | fprintf(stdout, "format: %s (%s)\n", 23 | genesis_audio_file_format_description(format), genesis_audio_file_format_name(format)); 24 | int codec_count = genesis_audio_file_format_codec_count(format); 25 | for (int codec_index = 0; codec_index < codec_count; codec_index += 1) { 26 | struct GenesisAudioFileCodec *codec = genesis_audio_file_format_codec_index(format, codec_index); 27 | fprintf(stdout, " %s\n", genesis_audio_file_codec_description(codec)); 28 | } 29 | } 30 | 31 | fprintf(stdout, "\nExport:\n"); 32 | int out_format_count = genesis_out_format_count(context); 33 | for (int format_index = 0; format_index < out_format_count; format_index += 1) { 34 | struct GenesisRenderFormat *format = genesis_out_format_index(context, format_index); 35 | fprintf(stdout, " %s\n", genesis_render_format_description(format)); 36 | } 37 | 38 | genesis_context_destroy(context); 39 | } 40 | -------------------------------------------------------------------------------- /src/render_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_RENDER_WIDGET_HPP 2 | #define GENESIS_RENDER_WIDGET_HPP 3 | 4 | #include "widget.hpp" 5 | #include "grid_layout_widget.hpp" 6 | #include "spacer_widget.hpp" 7 | 8 | struct Project; 9 | class TextWidget; 10 | class SelectWidget; 11 | class ButtonWidget; 12 | struct SettingsFile; 13 | struct RenderJob; 14 | class RenderWidget; 15 | 16 | struct RenderWidgetJob { 17 | RenderWidget *parent; 18 | TextWidget *done_text; 19 | ButtonWidget *stop_btn; 20 | RenderJob *render_job; 21 | }; 22 | 23 | class RenderWidget : public Widget { 24 | public: 25 | RenderWidget(GuiWindow *gui_window, Project *project, SettingsFile *settings_file); 26 | ~RenderWidget() override; 27 | void draw(const glm::mat4 &projection) override; 28 | void on_resize() override; 29 | 30 | void on_mouse_move(const MouseEvent *) override; 31 | 32 | 33 | Project *project; 34 | SettingsFile *settings_file; 35 | GridLayoutWidget main_layout; 36 | GridLayoutWidget props_layout; 37 | GridLayoutWidget jobs_layout; 38 | SpacerWidget spacer_widget; 39 | 40 | TextWidget *create_form_label(const char *text); 41 | 42 | List job_list; 43 | 44 | SelectWidget *output_format_select; 45 | SelectWidget *sample_format_select; 46 | SelectWidget *bit_rate_select; 47 | ButtonWidget *render_button; 48 | TextWidget *bit_rate_label; 49 | TextWidget *output_file_text; 50 | void select_settings_output_format(); 51 | void select_settings_sample_format(); 52 | void select_settings_bit_rate(); 53 | 54 | void my_on_selected_output_format_change(); 55 | void my_on_selected_sample_format_change(); 56 | void my_on_selected_bit_rate_change(); 57 | void my_on_render_activate(); 58 | void refresh_render_jobs(); 59 | 60 | void init_render_widget_job(RenderWidgetJob *rwj); 61 | void deinit_render_widget_job(RenderWidgetJob *rwj); 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /cmake/Findffmpeg.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Andrew Kelley 2 | # This file is MIT licensed. 3 | # See http://opensource.org/licenses/MIT 4 | 5 | # FFMPEG_FOUND 6 | # FFMPEG_INCLUDE_DIRS 7 | # FFMPEG_LIBRARIES 8 | 9 | # AVFORMAT_FOUND 10 | # AVFORMAT_INCLUDE_DIRS 11 | # AVFORMAT_LIBRARIES 12 | 13 | # AVCODEC_FOUND 14 | # AVCODEC_INCLUDE_DIRS 15 | # AVCODEC_LIBRARIES 16 | 17 | # AVUTIL_FOUND 18 | # AVUTIL_INCLUDE_DIRS 19 | # AVUTIL_LIBRARIES 20 | 21 | find_path(AVFORMAT_INCLUDE_DIRS NAMES libavformat/avformat.h) 22 | find_library(AVFORMAT_LIBRARIES NAMES avformat) 23 | if(AVFORMAT_LIBRARIES AND AVFORMAT_INCLUDE_DIRS) 24 | set(AVFORMAT_FOUND TRUE) 25 | else() 26 | set(AVFORMAT_FOUND FALSE) 27 | endif() 28 | 29 | find_path(AVCODEC_INCLUDE_DIRS NAMES libavcodec/avcodec.h) 30 | find_library(AVCODEC_LIBRARIES NAMES avcodec) 31 | if(AVCODEC_LIBRARIES AND AVCODEC_INCLUDE_DIRS) 32 | set(AVCODEC_FOUND TRUE) 33 | else() 34 | set(AVCODEC_FOUND FALSE) 35 | endif() 36 | 37 | find_path(AVUTIL_INCLUDE_DIRS NAMES libavutil/avutil.h) 38 | find_library(AVUTIL_LIBRARIES NAMES avutil) 39 | if(AVUTIL_LIBRARIES AND AVUTIL_INCLUDE_DIRS) 40 | set(AVUTIL_FOUND TRUE) 41 | else() 42 | set(AVUTIL_FOUND FALSE) 43 | endif() 44 | 45 | if(AVFORMAT_FOUND AND AVCODEC_FOUND AND AVUTIL_FOUND) 46 | set(FFMPEG_FOUND TRUE) 47 | set(FFMPEG_INCLUDE_DIRS 48 | ${AVFORMAT_INCLUDE_DIRS} 49 | ${AVCODEC_INCLUDE_DIRS} 50 | ${AVUTIL_INCLUDE_DIRS}) 51 | set(FFMPEG_LIBRARIES 52 | ${AVFORMAT_LIBRARIES} 53 | ${AVCODEC_LIBRARIES} 54 | ${AVUTIL_LIBRARIES}) 55 | else() 56 | set(FFMPEG_FOUND FALSE) 57 | endif() 58 | 59 | include(FindPackageHandleStandardArgs) 60 | find_package_handle_standard_args(FFMPEG DEFAULT_MSG 61 | AVFORMAT_LIBRARIES AVFORMAT_INCLUDE_DIRS 62 | AVCODEC_LIBRARIES AVCODEC_INCLUDE_DIRS 63 | AVUTIL_LIBRARIES AVUTIL_INCLUDE_DIRS) 64 | 65 | mark_as_advanced( 66 | AVFORMAT_INCLUDE_DIRS AVFORMAT_LIBRARIES 67 | AVCODEC_INCLUDE_DIRS AVCODEC_LIBRARIES 68 | AVUTIL_INCLUDE_DIRS AVUTIL_LIBRARIES) 69 | -------------------------------------------------------------------------------- /src/render_job.cpp: -------------------------------------------------------------------------------- 1 | #include "genesis.h" 2 | #include "byte_buffer.hpp" 3 | #include "render_job.hpp" 4 | #include "audio_graph.hpp" 5 | #include "gui.hpp" 6 | 7 | static void on_render_job_updated(Event, void *userdata) { 8 | RenderJob *rj = (RenderJob *)userdata; 9 | assert(rj); 10 | 11 | if (rj->audio_graph->render_frame_index == rj->audio_graph->render_frame_count) { 12 | rj->is_complete = true; 13 | render_job_stop(rj); 14 | } 15 | 16 | rj->gui->events.trigger(EventRenderJobsUpdated); 17 | } 18 | 19 | void render_job_init(RenderJob *rj, Project *project, GenesisContext *genesis_context, Gui *gui) { 20 | assert(rj); 21 | rj->project = project; 22 | rj->audio_graph = nullptr; 23 | rj->genesis_context = genesis_context; 24 | rj->gui = gui; 25 | rj->is_complete = false; 26 | } 27 | 28 | void render_job_deinit(RenderJob *rj) { 29 | assert(rj); 30 | render_job_stop(rj); 31 | } 32 | 33 | void render_job_start(RenderJob *rj, const GenesisExportFormat *export_format, 34 | const ByteBuffer &out_path) 35 | { 36 | assert(rj); 37 | audio_graph_create_render(rj->project, rj->genesis_context, export_format, out_path, 38 | &rj->audio_graph); 39 | 40 | rj->audio_graph->events.attach_handler(EventAudioGraphPlayHeadChanged, on_render_job_updated, rj); 41 | 42 | audio_graph_start_pipeline(rj->audio_graph); 43 | } 44 | 45 | void render_job_stop(RenderJob *rj) { 46 | assert(rj); 47 | audio_graph_destroy(rj->audio_graph); 48 | rj->audio_graph = nullptr; 49 | } 50 | 51 | float render_job_progress(RenderJob *rj) { 52 | assert(rj); 53 | assert(rj->audio_graph); 54 | double nominator = rj->audio_graph->render_frame_index; 55 | double denominator = rj->audio_graph->render_frame_count; 56 | return nominator / denominator; 57 | } 58 | 59 | bool render_job_is_complete(RenderJob *rj) { 60 | assert(rj); 61 | return rj->is_complete; 62 | } 63 | 64 | void render_job_flush_events(RenderJob *rj) { 65 | if (rj->audio_graph) 66 | audio_graph_flush_events(rj->audio_graph); 67 | } 68 | -------------------------------------------------------------------------------- /src/color.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COLOR_HPP 2 | #define COLOR_HPP 3 | 4 | #include "glm.hpp" 5 | 6 | #include 7 | 8 | static glm::vec4 parse_color(const char *color) { 9 | if (color[0] == '#') 10 | color += 1; 11 | size_t len = strlen(color); 12 | glm::vec4 out; 13 | unsigned r, g, b, a; 14 | if (len == 6) { 15 | sscanf(color, "%2x%2x%2x", &r, &g, &b); 16 | out[0] = (r / 255.0f); 17 | out[1] = (g / 255.0f); 18 | out[2] = (b / 255.0f); 19 | out[3] = 1.0f; 20 | } else if (len == 8) { 21 | sscanf(color, "%2x%2x%2x%2x", &r, &g, &b, &a); 22 | out[0] = (r / 255.0f); 23 | out[1] = (g / 255.0f); 24 | out[2] = (b / 255.0f); 25 | out[3] = (a / 255.0f); 26 | } else { 27 | panic("invalid color length"); 28 | } 29 | return out; 30 | } 31 | 32 | static inline glm::vec4 color_fg_text(void) { 33 | return parse_color("#DCDCDC"); 34 | } 35 | 36 | static inline glm::vec4 color_light_disabled_text(void) { 37 | return parse_color("#C7C7C7"); 38 | } 39 | 40 | static inline glm::vec4 color_light_bg(void) { 41 | return parse_color("#949494"); 42 | } 43 | 44 | static inline glm::vec4 color_dark_bg(void) { 45 | return parse_color("#333333"); 46 | } 47 | 48 | static inline glm::vec4 color_dark_bg_highlight(void) { 49 | return parse_color("#404040"); 50 | } 51 | 52 | static inline glm::vec4 color_dark_bg_alt(void) { 53 | return parse_color("#2E5986"); 54 | } 55 | 56 | static inline glm::vec4 color_dark_text(void) { 57 | return parse_color("#232323"); 58 | } 59 | 60 | static inline glm::vec4 color_dark_border(void) { 61 | return parse_color("#1B1B1B"); 62 | } 63 | 64 | static inline glm::vec4 color_light_border(void) { 65 | return parse_color("#868686"); 66 | } 67 | 68 | static inline glm::vec4 color_dark_bg_inactive(void) { 69 | return parse_color("#777777"); 70 | } 71 | 72 | static inline glm::vec4 color_attention_overlay(void) { 73 | return parse_color("#D82B2B"); 74 | } 75 | 76 | static inline glm::vec4 color_selection(void) { 77 | return parse_color("#254385"); 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/string.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STRING_HPP 2 | #define STRING_HPP 3 | 4 | #include "list.hpp" 5 | #include "byte_buffer.hpp" 6 | 7 | #include 8 | 9 | // UTF-8 string 10 | class String { 11 | public: 12 | String() {} 13 | String(const char *); 14 | String(const String ©); 15 | String& operator= (const String& other); 16 | String(const ByteBuffer &bytes); 17 | String(const ByteBuffer &bytes, bool *ok); 18 | 19 | static String decode(const ByteBuffer &bytes, bool *ok); 20 | // this one panics if the string is invalid 21 | static String decode(const ByteBuffer &bytes); 22 | ByteBuffer encode() const; 23 | 24 | int length() const { 25 | return _chars.length(); 26 | } 27 | const uint32_t & at(int index) const { 28 | return _chars.at(index); 29 | } 30 | uint32_t & at(int index) { 31 | return _chars.at(index); 32 | } 33 | 34 | void make_lower_case(); 35 | void make_upper_case(); 36 | 37 | static const int max_codepoint = 0x1fffff; 38 | void append(uint32_t c) { 39 | if (c > max_codepoint) 40 | panic("codepoint out of UTF-8 range"); 41 | ok_or_panic(_chars.append(c)); 42 | } 43 | void append(String s); 44 | String substring(int start, int end) const; 45 | String substring(int start) const; 46 | 47 | void replace(int start, int end, String s); 48 | 49 | void split_on_whitespace(List &out) const; 50 | 51 | off_t index_of_insensitive(const String &other) const; 52 | 53 | void remove_range(int start, int end) { 54 | _chars.remove_range(start, end); 55 | } 56 | 57 | int allocated_size() const { 58 | return _chars.allocated_size(); 59 | } 60 | 61 | static bool equal(const String &a, const String &b); 62 | static int compare(const String &a, const String &b); 63 | static int compare_insensitive(const String &a, const String &b); 64 | static uint32_t char_to_lower(uint32_t c); 65 | static uint32_t char_to_upper(uint32_t c); 66 | 67 | static bool is_whitespace(uint32_t c); 68 | 69 | private: 70 | List _chars; 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/label.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LABEL_HPP 2 | #define LABEL_HPP 3 | 4 | #include "freetype.hpp" 5 | #include "string.hpp" 6 | #include "glm.hpp" 7 | #include "font_size.hpp" 8 | #include "glfw.hpp" 9 | 10 | class Gui; 11 | class GuiWindow; 12 | 13 | class Label { 14 | public: 15 | Label(Gui *gui); 16 | ~Label(); 17 | 18 | // need to call update() to make it take effect 19 | void set_text(const String &text) { 20 | _text = text; 21 | } 22 | const String &text() const { 23 | return _text; 24 | } 25 | String &text() { 26 | return _text; 27 | } 28 | // need to call update() to make it take effect 29 | void set_font_size(int size); 30 | 31 | void update(); 32 | 33 | int width() const { 34 | return _width; 35 | } 36 | 37 | int height() const { 38 | return _height; 39 | } 40 | 41 | void draw(const glm::mat4 &mvp, const glm::vec4 &color); 42 | 43 | int cursor_at_pos(int x, int y) const; 44 | void pos_at_cursor(int index, int &x, int &y) const; 45 | void get_slice_dimensions(int start, int end, int &start_x, int &end_x) const; 46 | 47 | int above_size() const { 48 | return _font_size->_max_above_size; 49 | } 50 | 51 | int below_size() const { 52 | return _font_size->_max_below_size; 53 | } 54 | 55 | void replace_text(int start, int end, String text); 56 | 57 | private: 58 | struct Letter { 59 | uint32_t codepoint; 60 | 61 | int left; // half-way between prev letter and this one. 0 for first letter 62 | int bitmap_left; // left + bitmap_left is the first pixel of the letter 63 | int bitmap_width; // left + bitmap_left + bitmap_width is the last pixel of the letter 64 | int full_width; // left + full_width is half-way between this letter and next 65 | 66 | int above_size; 67 | int below_size; 68 | int bitmap_top; 69 | }; 70 | 71 | Gui *_gui; 72 | int _width; 73 | int _height; 74 | GLuint _texture_id; 75 | GLuint _vertex_buffer; 76 | 77 | String _text; 78 | FontSize *_font_size; 79 | 80 | ByteBuffer _img_buffer; 81 | 82 | // cached from _text on update() 83 | List _letters; 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WIDGET_HPP 2 | #define WIDGET_HPP 3 | 4 | #include "glm.hpp" 5 | #include "mouse_event.hpp" 6 | #include "key_event.hpp" 7 | #include "drag_event.hpp" 8 | 9 | class GuiWindow; 10 | class Gui; 11 | class ContextMenuWidget; 12 | class MenuWidgetItem; 13 | 14 | class Widget { 15 | public: 16 | Widget(GuiWindow *gui_window); 17 | virtual ~Widget(); 18 | virtual void draw(const glm::mat4 &projection) = 0; 19 | 20 | // default no minimum, no maximum 21 | virtual int min_width() const { return 0; } 22 | virtual int max_width() const { return -1; } 23 | virtual int min_height() const { return 0; } 24 | virtual int max_height() const { return -1; } 25 | 26 | // call when one of the above 4 functions values will be different 27 | void on_size_hints_changed(); 28 | 29 | // return true if you ate the event 30 | virtual void on_mouse_move(const MouseEvent *) {} 31 | virtual void on_mouse_out(const MouseEvent *) {} 32 | virtual void on_mouse_over(const MouseEvent *) {} 33 | virtual void on_drag(const DragEvent *) {} 34 | virtual void on_gain_focus() {} 35 | virtual void on_lose_focus() {} 36 | virtual void on_text_input(const TextInputEvent *event) {} 37 | virtual bool on_key_event(const KeyEvent *) { return false; } 38 | virtual void on_mouse_wheel(const MouseWheelEvent *) {} 39 | virtual void on_resize() {} 40 | virtual void remove_widget(Widget *widget); 41 | 42 | 43 | GuiWindow *gui_window; 44 | Widget *parent_widget; 45 | Gui *gui; 46 | 47 | int left; 48 | int top; 49 | int width; 50 | int height; 51 | 52 | // index into grid layout widget parent, if any 53 | int layout_row; 54 | int layout_col; 55 | bool is_visible; 56 | 57 | // convenience methods 58 | glm::mat4 transform2d(int left, int top, float scale_x, float scale_y); 59 | glm::mat4 transform2d(int left, int top); 60 | ContextMenuWidget *pop_context_menu(MenuWidgetItem *menu_widget_item, int left, int top, int width, int height); 61 | bool forward_drag_event(Widget *widget, const DragEvent *event); 62 | bool forward_mouse_event(Widget *widget, const MouseEvent *event); 63 | bool forward_mouse_wheel_event(Widget *widget, const MouseWheelEvent *event); 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/font_size.cpp: -------------------------------------------------------------------------------- 1 | #include "font_size.hpp" 2 | 3 | static void ft_ok(FT_Error err) { 4 | if (err) 5 | panic("freetype error"); 6 | } 7 | 8 | FontSize::FontSize(FT_Face font_face, int font_size) : 9 | _max_above_size(0), 10 | _max_below_size(0), 11 | _font_face(font_face), 12 | _font_size(font_size) 13 | { 14 | // pre-fill some characters in the cache so that we have a good measurement of 15 | // _max_above_size and _max_below_size 16 | static const char * some_characters = 17 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%^&*()[{]}?/|.<>?"; 18 | for (const char *ptr = some_characters; *ptr; ptr += 1) 19 | font_cache_entry((uint32_t)*ptr); 20 | } 21 | 22 | FontSize::~FontSize() { 23 | auto it = _font_cache.entry_iterator(); 24 | for (;;) { 25 | auto *entry = it.next(); 26 | if (!entry) 27 | break; 28 | FontCacheValue *font_cache_value = &entry->value; 29 | FT_Done_Glyph(font_cache_value->glyph); 30 | } 31 | } 32 | 33 | uint32_t hash_uint32_t(const uint32_t &x) { 34 | return x; 35 | } 36 | 37 | FontCacheValue FontSize::font_cache_entry(uint32_t codepoint) { 38 | auto *entry = _font_cache.maybe_get(codepoint); 39 | if (entry) 40 | return entry->value; 41 | 42 | ft_ok(FT_Set_Char_Size(_font_face, 0, _font_size * 64, 0, 0)); 43 | FT_UInt glyph_index = FT_Get_Char_Index(_font_face, codepoint); 44 | ft_ok(FT_Load_Glyph(_font_face, glyph_index, FT_LOAD_RENDER)); 45 | FT_GlyphSlot glyph_slot = _font_face->glyph; 46 | FT_Glyph glyph; 47 | ft_ok(FT_Get_Glyph(glyph_slot, &glyph)); 48 | ft_ok(FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 0)); 49 | FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) glyph; 50 | 51 | int bmp_start_top = bitmap_glyph->top; 52 | int bmp_height = bitmap_glyph->bitmap.rows; 53 | int this_above_size = bmp_start_top; 54 | int this_below_size = bmp_height - this_above_size; 55 | 56 | if (this_above_size > _max_above_size) 57 | _max_above_size = this_above_size; 58 | if (this_below_size > _max_below_size) 59 | _max_below_size = this_below_size; 60 | 61 | FontCacheValue value = FontCacheValue{glyph, bitmap_glyph, glyph_index, this_above_size, this_below_size}; 62 | _font_cache.put(codepoint, value); 63 | return value; 64 | } 65 | -------------------------------------------------------------------------------- /src/button_widget.cpp: -------------------------------------------------------------------------------- 1 | #include "button_widget.hpp" 2 | #include "gui_window.hpp" 3 | #include "color.hpp" 4 | 5 | 6 | ButtonWidget::~ButtonWidget() { 7 | 8 | } 9 | 10 | ButtonWidget::ButtonWidget(GuiWindow *gui_window) : 11 | Widget(gui_window), 12 | label(gui_window->gui) 13 | { 14 | padding_left = 8; 15 | padding_right = 8; 16 | padding_top = 8; 17 | padding_bottom = 8; 18 | 19 | hovering = false; 20 | mouse_down = false; 21 | text_color = color_fg_text(); 22 | 23 | update_model(); 24 | } 25 | 26 | void ButtonWidget::draw(const glm::mat4 &projection) { 27 | bg.draw(gui_window, projection); 28 | label.draw(projection * label_model, text_color); 29 | } 30 | 31 | void ButtonWidget::on_mouse_move(const MouseEvent *event) { 32 | switch (event->action) { 33 | case MouseActionDown: 34 | if (event->button == MouseButtonLeft) { 35 | mouse_down = true; 36 | update_model(); 37 | } 38 | break; 39 | case MouseActionUp: 40 | if (event->button == MouseButtonLeft) { 41 | mouse_down = false; 42 | update_model(); 43 | events.trigger(EventActivate); 44 | } 45 | break; 46 | case MouseActionMove: 47 | break; 48 | } 49 | } 50 | 51 | void ButtonWidget::on_mouse_out(const MouseEvent *event) { 52 | hovering = false; 53 | } 54 | 55 | void ButtonWidget::on_mouse_over(const MouseEvent *event) { 56 | hovering = true; 57 | } 58 | 59 | void ButtonWidget::set_text(const String &text) { 60 | label.set_text(text); 61 | label.update(); 62 | update_model(); 63 | } 64 | 65 | void ButtonWidget::update_model() { 66 | bg.set_scheme(mouse_down ? SunkenBoxSchemeSunkenBorders : SunkenBoxSchemeRaisedBorders); 67 | bg.update(this, 0, 0, width, height); 68 | 69 | label_model = transform2d(width / 2 - label.width() / 2, height / 2 - label.height() / 2); 70 | } 71 | 72 | int ButtonWidget::min_width() const { 73 | return padding_left + label.width() + padding_right; 74 | } 75 | 76 | int ButtonWidget::max_width() const { 77 | return min_width(); 78 | } 79 | 80 | int ButtonWidget::min_height() const { 81 | return padding_top + label.height() + padding_bottom; 82 | } 83 | 84 | int ButtonWidget::max_height() const { 85 | return min_height(); 86 | } 87 | -------------------------------------------------------------------------------- /src/error.cpp: -------------------------------------------------------------------------------- 1 | #include "error.h" 2 | #include "util.hpp" 3 | 4 | const char *genesis_strerror(int error) { 5 | switch ((GenesisError)error) { 6 | case GenesisErrorNone: return "(no error)"; 7 | case GenesisErrorNoMem: return "out of memory"; 8 | case GenesisErrorMaxChannelsExceeded: return "max channels exceeded"; 9 | case GenesisErrorIncompatiblePorts: return "incompatible ports"; 10 | case GenesisErrorInvalidPortDirection: return "invalid port direction"; 11 | case GenesisErrorIncompatibleSampleRates: return "incompatible sample rates"; 12 | case GenesisErrorIncompatibleChannelLayouts: return "incompatible channel layouts"; 13 | case GenesisErrorOpeningMidiHardware: return "opening midi hardware"; 14 | case GenesisErrorOpeningAudioHardware: return "opening audio hardware"; 15 | case GenesisErrorInvalidState: return "invalid state"; 16 | case GenesisErrorSystemResources: return "system resource not available"; 17 | case GenesisErrorDecodingAudio: return "decoding audio"; 18 | case GenesisErrorInvalidParam: return "invalid parameter"; 19 | case GenesisErrorInvalidPortType: return "invalid port type"; 20 | case GenesisErrorPortNotFound: return "port not found"; 21 | case GenesisErrorNoAudioFound: return "no audio found"; 22 | case GenesisErrorUnimplemented: return "unimplemented (patch welcome!)"; 23 | case GenesisErrorAborted: return "aborted"; 24 | case GenesisErrorFileAccess: return "file access"; 25 | case GenesisErrorInvalidFormat: return "invalid format"; 26 | case GenesisErrorEmptyFile: return "empty file"; 27 | case GenesisErrorKeyNotFound: return "key not found"; 28 | case GenesisErrorFileNotFound: return "file not found"; 29 | case GenesisErrorPermissionDenied: return "permission denied"; 30 | case GenesisErrorNotDir: return "not a directory"; 31 | case GenesisErrorNoDecoderFound: return "no decoder found"; 32 | case GenesisErrorAlreadyExists: return "already exists"; 33 | case GenesisErrorConnectionRefused: return "connection refused"; 34 | case GenesisErrorIncompatibleDevice: return "incompatible device"; 35 | case GenesisErrorDeviceNotFound: return "device not found"; 36 | case GenesisErrorDecodingString: return "decoding string"; 37 | } 38 | panic("invalid error enum value"); 39 | } 40 | -------------------------------------------------------------------------------- /src/texture.cpp: -------------------------------------------------------------------------------- 1 | #include "texture.hpp" 2 | #include "debug_gl.hpp" 3 | #include "gui.hpp" 4 | #include "gui_window.hpp" 5 | 6 | Texture::Texture(Gui *gui) : 7 | _gui(gui), 8 | _width(0), 9 | _height(0) 10 | { 11 | glGenTextures(1, &_texture_id); 12 | glBindTexture(GL_TEXTURE_2D, _texture_id); 13 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 14 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 15 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 16 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 17 | } 18 | 19 | Texture::~Texture() { 20 | glDeleteTextures(1, &_texture_id); 21 | } 22 | 23 | void Texture::send_pixels(const ByteBuffer &pixels, int width, int height) { 24 | _width = width; 25 | _height = height; 26 | 27 | if (pixels.length() != width * height * 4) 28 | panic("invalid pixels length"); 29 | 30 | glActiveTexture(GL_TEXTURE0); 31 | glBindTexture(GL_TEXTURE_2D, _texture_id); 32 | glPixelStorei(GL_PACK_ALIGNMENT, 1); 33 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 34 | 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.raw()); 35 | 36 | assert_no_gl_error(); 37 | } 38 | 39 | void Texture::draw(GuiWindow *window, const glm::mat4 &mvp) { 40 | _gui->_shader_program_manager._texture_shader_program.bind(); 41 | 42 | _gui->_shader_program_manager._texture_shader_program.set_uniform( 43 | _gui->_shader_program_manager._texture_uniform_tex, 0); 44 | 45 | _gui->_shader_program_manager._texture_shader_program.set_uniform( 46 | _gui->_shader_program_manager._texture_uniform_mvp, mvp); 47 | 48 | glBindBuffer(GL_ARRAY_BUFFER, _gui->_static_geometry._rect_2d_vertex_buffer); 49 | glEnableVertexAttribArray(_gui->_shader_program_manager._texture_attrib_position); 50 | glVertexAttribPointer(_gui->_shader_program_manager._texture_attrib_position, 3, GL_FLOAT, GL_FALSE, 0, NULL); 51 | 52 | glBindBuffer(GL_ARRAY_BUFFER, _gui->_static_geometry._rect_2d_tex_coord_buffer); 53 | glEnableVertexAttribArray(_gui->_shader_program_manager._texture_attrib_tex_coord); 54 | glVertexAttribPointer(_gui->_shader_program_manager._texture_attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, 0, NULL); 55 | 56 | glActiveTexture(GL_TEXTURE0); 57 | glBindTexture(GL_TEXTURE_2D, _texture_id); 58 | 59 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 60 | } 61 | -------------------------------------------------------------------------------- /src/grid_layout_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GRID_LAYOUT_HPP 2 | #define GRID_LAYOUT_HPP 3 | 4 | #include "widget.hpp" 5 | #include "list.hpp" 6 | 7 | enum HAlign { 8 | HAlignLeft, 9 | HAlignRight, 10 | HAlignCenter, 11 | }; 12 | 13 | enum VAlign { 14 | VAlignTop, 15 | VAlignBottom, 16 | VAlignCenter, 17 | }; 18 | 19 | class GridLayoutWidget : public Widget { 20 | public: 21 | GridLayoutWidget(GuiWindow *gui_window) : 22 | Widget(gui_window), 23 | spacing(4), 24 | padding(4) 25 | { 26 | } 27 | ~GridLayoutWidget() override { } 28 | 29 | int rows() const { 30 | return cells.length(); 31 | } 32 | 33 | int cols() const { 34 | return (cells.length() > 0) ? cells.at(0).length() : 0; 35 | } 36 | 37 | void draw(const glm::mat4 &projection) override; 38 | void add_widget(Widget *widget, int row, int col, HAlign h_align, VAlign v_align); 39 | void remove_widget(Widget *widget) override; 40 | void remove_all_widgets(); 41 | 42 | int min_width() const override; 43 | int max_width() const override; 44 | int min_height() const override; 45 | int max_height() const override; 46 | 47 | void on_resize() override; 48 | 49 | void on_mouse_move(const MouseEvent *) override; 50 | void on_drag(const DragEvent *) override; 51 | 52 | struct Cell { 53 | Widget *widget; 54 | HAlign h_align; 55 | VAlign v_align; 56 | }; 57 | 58 | int spacing; 59 | int padding; 60 | List> cells; 61 | 62 | struct ColRowInfo { 63 | bool done; 64 | int min_size; // min_width/min_height 65 | int max_size; // max_width/max_height 66 | int size; // width/height 67 | int start; // left/top 68 | }; 69 | List col_props; 70 | List row_props; 71 | 72 | void ensure_size(int row_count, int col_count); 73 | void reduce_size(); 74 | 75 | int get_row_min_width(int row) const; 76 | int get_row_max_width(int row) const; 77 | int get_row_min_height(int row) const; 78 | int get_row_max_height(int row) const; 79 | int get_col_min_width(int col) const; 80 | int get_col_max_width(int col) const; 81 | int get_col_min_height(int col) const; 82 | 83 | int get_col_max_height(int col) const; 84 | 85 | void layout_x(); 86 | void layout_y(); 87 | 88 | bool expanding_x() const; 89 | bool expanding_y() const; 90 | }; 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /src/alpha_texture.cpp: -------------------------------------------------------------------------------- 1 | #include "alpha_texture.hpp" 2 | #include "debug_gl.hpp" 3 | #include "gui.hpp" 4 | 5 | AlphaTexture::AlphaTexture(Gui *gui) : 6 | _gui(gui), 7 | _width(0), 8 | _height(0) 9 | { 10 | glGenTextures(1, &_texture_id); 11 | glBindTexture(GL_TEXTURE_2D, _texture_id); 12 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 13 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 14 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 15 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 16 | } 17 | 18 | AlphaTexture::~AlphaTexture() { 19 | glDeleteTextures(1, &_texture_id); 20 | } 21 | 22 | void AlphaTexture::send_pixels(const ByteBuffer &pixels, int width, int height) { 23 | _width = width; 24 | _height = height; 25 | 26 | if (pixels.length() != width * height) 27 | panic("expected pixel length: %d received pixel length: %d", width * height, pixels.length()); 28 | 29 | glActiveTexture(GL_TEXTURE0); 30 | glBindTexture(GL_TEXTURE_2D, _texture_id); 31 | glPixelStorei(GL_PACK_ALIGNMENT, 1); 32 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 33 | 0, GL_RED, GL_UNSIGNED_BYTE, pixels.raw()); 34 | 35 | assert_no_gl_error(); 36 | } 37 | 38 | void AlphaTexture::draw(GuiWindow *window, const glm::vec4 &color, const glm::mat4 &mvp) { 39 | _gui->_shader_program_manager._text_shader_program.bind(); 40 | 41 | _gui->_shader_program_manager._text_shader_program.set_uniform( 42 | _gui->_shader_program_manager._text_uniform_color, color); 43 | 44 | _gui->_shader_program_manager._text_shader_program.set_uniform( 45 | _gui->_shader_program_manager._text_uniform_tex, 0); 46 | 47 | _gui->_shader_program_manager._text_shader_program.set_uniform( 48 | _gui->_shader_program_manager._text_uniform_mvp, mvp); 49 | 50 | glBindBuffer(GL_ARRAY_BUFFER, _gui->_static_geometry._rect_2d_vertex_buffer); 51 | glEnableVertexAttribArray(_gui->_shader_program_manager._text_attrib_position); 52 | glVertexAttribPointer(_gui->_shader_program_manager._text_attrib_position, 3, GL_FLOAT, GL_FALSE, 0, NULL); 53 | 54 | glBindBuffer(GL_ARRAY_BUFFER, _gui->_static_geometry._rect_2d_tex_coord_buffer); 55 | glEnableVertexAttribArray(_gui->_shader_program_manager._text_attrib_tex_coord); 56 | glVertexAttribPointer(_gui->_shader_program_manager._text_attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, 0, NULL); 57 | 58 | glActiveTexture(GL_TEXTURE0); 59 | glBindTexture(GL_TEXTURE_2D, _texture_id); 60 | 61 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/tab_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TAB_WIDGET_HPP 2 | #define TAB_WIDGET_HPP 3 | 4 | #include "widget.hpp" 5 | #include "sunken_box.hpp" 6 | 7 | class Label; 8 | 9 | struct TabWidgetTab { 10 | Widget *widget; 11 | Label *label; 12 | int left; 13 | int right; 14 | SunkenBox bg; 15 | glm::mat4 label_model; 16 | glm::mat4 left_line_model; 17 | glm::mat4 right_line_model; 18 | glm::mat4 top_line_model; 19 | bool is_current; 20 | int index; 21 | }; 22 | 23 | class TabWidget : public Widget { 24 | public: 25 | TabWidget(GuiWindow *window); 26 | ~TabWidget() override; 27 | 28 | void draw(const glm::mat4 &projection) override; 29 | 30 | void on_resize() override { 31 | update_model(); 32 | } 33 | 34 | // if true, hide the tab bar when there are less than 2 tabs 35 | void set_auto_hide(bool value) { 36 | auto_hide = value; 37 | update_model(); 38 | } 39 | 40 | void select_index(int index); 41 | void select_widget(Widget *widget); 42 | 43 | void on_mouse_move(const MouseEvent *event) override; 44 | void on_drag(const DragEvent *event) override; 45 | bool on_key_event(const KeyEvent *event) override; 46 | 47 | void add_widget(Widget *widget, const String &title); 48 | 49 | void move_tab(int source_index, int dest_index); 50 | void remove_tab(int index); 51 | void insert_tab(Widget *widget, const String &title, int insert_index); 52 | 53 | int tab_count() const { 54 | return tabs.length(); 55 | } 56 | 57 | List tabs; 58 | int current_index; 59 | TabWidgetTab *current_tab; 60 | glm::mat4 tab_bottom_line_model; 61 | glm::vec4 tab_border_color; 62 | int tab_height; 63 | glm::vec4 bg_color; 64 | glm::vec4 tab_bg_color; 65 | glm::mat4 tab_bg_model; 66 | glm::vec4 tab_text_color; 67 | glm::vec4 tab_selected_bg_color; 68 | int padding_left; 69 | int padding_right; 70 | int padding_top; 71 | int padding_bottom; 72 | int tab_spacing; 73 | int title_padding_left; 74 | int title_padding_right; 75 | int widget_top; 76 | bool auto_hide; 77 | bool show_tab_bar; 78 | void (*on_drag_tab)(TabWidgetTab *tab, TabWidget *tab_widget, const MouseEvent *event); 79 | void (*on_drag_event)(TabWidget *tab_widget, const DragEvent *event); 80 | 81 | void update_model(); 82 | TabWidgetTab *get_tab_at(int x, int y); 83 | int get_insert_index_at(int x, int y); 84 | void change_current_index(int direction); 85 | int get_widget_index(Widget *widget); 86 | void get_tab_pos(int index, int *x, int *y); 87 | void clamp_current_index(); 88 | }; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /src/ordered_map_file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ORDERED_MAP_FILE_HPP 2 | #define ORDERED_MAP_FILE_HPP 3 | 4 | #include "genesis.h" 5 | #include "os.hpp" 6 | #include "list.hpp" 7 | #include "byte_buffer.hpp" 8 | #include "locked_queue.hpp" 9 | #include "hash_map.hpp" 10 | #include "atomics.hpp" 11 | 12 | struct OrderedMapFile; 13 | 14 | struct OrderedMapFileEntry { 15 | ByteBuffer key; 16 | int offset; 17 | int size; 18 | }; 19 | 20 | struct OrderedMapFileBuffer { 21 | char *data; 22 | int size; 23 | }; 24 | 25 | struct OrderedMapFilePut { 26 | OrderedMapFileBuffer *key; 27 | OrderedMapFileBuffer *value; 28 | }; 29 | 30 | struct OrderedMapFileDel { 31 | OrderedMapFileBuffer *key; 32 | }; 33 | 34 | struct OrderedMapFileBatch { 35 | OrderedMapFile *omf; 36 | List puts; 37 | List dels; 38 | }; 39 | 40 | struct OrderedMapFile { 41 | OsThread *write_thread; 42 | OsMutex *mutex; 43 | OsCond *cond; 44 | ByteBuffer write_buffer; 45 | atomic_bool running; 46 | LockedQueue queue; 47 | FILE *file; 48 | long transaction_offset; 49 | List *list; 50 | HashMap *map; 51 | }; 52 | 53 | int ordered_map_file_open(const char *path, OrderedMapFile **omf); 54 | void ordered_map_file_done_reading(OrderedMapFile *omf); 55 | void ordered_map_file_close(OrderedMapFile *omf); 56 | 57 | OrderedMapFileBatch *ordered_map_file_batch_create(OrderedMapFile *omf); 58 | void ordered_map_file_batch_destroy(OrderedMapFileBatch *batch); 59 | // transfers ownership of the batch 60 | int ordered_map_file_batch_exec(OrderedMapFileBatch *batch); 61 | 62 | OrderedMapFileBuffer *ordered_map_file_buffer_create(int size); 63 | void ordered_map_file_buffer_destroy(OrderedMapFileBuffer *buffer); 64 | // put and del transfer ownership of the buffers 65 | int ordered_map_file_batch_put(OrderedMapFileBatch *batch, OrderedMapFileBuffer *key, OrderedMapFileBuffer *value); 66 | int ordered_map_file_batch_del(OrderedMapFileBatch *batch, OrderedMapFileBuffer *key); 67 | 68 | // these reading functions are only valid after opening the file, until you call 69 | // ordered_map_file_done_reading. then only write functions can be called. 70 | int ordered_map_file_count(OrderedMapFile *omf); 71 | int ordered_map_file_find_key(OrderedMapFile *omf, const ByteBuffer &key); 72 | int ordered_map_file_find_prefix(OrderedMapFile *omf, const ByteBuffer &prefix); 73 | int ordered_map_file_get(OrderedMapFile *omf, int index, ByteBuffer **out_key, ByteBuffer &out_value); 74 | 75 | 76 | // blocks until all queued writes finish 77 | // automatically called by ordered_map_file_close 78 | void ordered_map_file_flush(OrderedMapFile *omf); 79 | 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /src/png_image.cpp: -------------------------------------------------------------------------------- 1 | #include "png_image.hpp" 2 | #include "glfw.hpp" 3 | 4 | #include 5 | 6 | struct PngIo { 7 | long index; 8 | unsigned char *buffer; 9 | long size; 10 | }; 11 | 12 | static void read_png_data(png_structp png_ptr, png_bytep data, png_size_t length) { 13 | PngIo *png_io = reinterpret_cast(png_get_io_ptr(png_ptr)); 14 | long new_index = png_io->index + length; 15 | if (new_index > png_io->size) 16 | panic("libpng trying to read beyond buffer"); 17 | memcpy(data, png_io->buffer + png_io->index, length); 18 | png_io->index = new_index; 19 | } 20 | 21 | PngImage::PngImage(const ByteBuffer &compressed_bytes) { 22 | if (png_sig_cmp((png_bytep)compressed_bytes.raw(), 0, 8)) 23 | panic("not png file"); 24 | 25 | png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 26 | if (!png_ptr) 27 | panic("unable to create png read struct"); 28 | 29 | png_infop info_ptr = png_create_info_struct(png_ptr); 30 | if (!info_ptr) 31 | panic("unable to create png info struct"); 32 | 33 | // don't call any png_* functions outside of this function D: 34 | if (setjmp(png_jmpbuf(png_ptr))) 35 | panic("libpng has jumped the shark"); 36 | 37 | png_set_sig_bytes(png_ptr, 8); 38 | 39 | PngIo png_io = {8, (unsigned char *)compressed_bytes.raw(), compressed_bytes.length()}; 40 | png_set_read_fn(png_ptr, &png_io, read_png_data); 41 | 42 | png_read_info(png_ptr, info_ptr); 43 | 44 | _width = png_get_image_width(png_ptr, info_ptr); 45 | _height = png_get_image_height(png_ptr, info_ptr); 46 | 47 | if (_width <= 0 || _height <= 0) 48 | panic("spritesheet image has no pixels"); 49 | 50 | // bits per channel (not per pixel) 51 | int bits_per_channel = png_get_bit_depth(png_ptr, info_ptr); 52 | if (bits_per_channel != 8) 53 | panic("expected 8 bits per channel"); 54 | 55 | int channel_count = png_get_channels(png_ptr, info_ptr); 56 | if (channel_count != 4) 57 | panic("expected 4 channels"); 58 | 59 | int color_type = png_get_color_type(png_ptr, info_ptr); 60 | if (color_type != PNG_COLOR_TYPE_RGBA) 61 | panic("expected RGBA"); 62 | 63 | _pitch = _width * bits_per_channel * channel_count / 8; 64 | _image_data.resize(_height * _pitch); 65 | png_bytep *row_ptrs = ok_mem(allocate_zero(_height)); 66 | 67 | for (int i = 0; i < _height; i++) { 68 | png_uint_32 q = (_height - i - 1) * _pitch; 69 | row_ptrs[i] = (png_bytep)_image_data.raw() + q; 70 | } 71 | 72 | png_read_image(png_ptr, row_ptrs); 73 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 74 | destroy(row_ptrs, _height); 75 | } 76 | 77 | void PngImage::gl_pixel_store_alignment() const { 78 | glPixelStorei(GL_PACK_ALIGNMENT, 4); 79 | } 80 | -------------------------------------------------------------------------------- /src/sunken_box.cpp: -------------------------------------------------------------------------------- 1 | #include "sunken_box.hpp" 2 | #include "color.hpp" 3 | #include "gui_window.hpp" 4 | 5 | SunkenBox::SunkenBox() { 6 | set_scheme(SunkenBoxSchemeSunken); 7 | } 8 | 9 | void SunkenBox::set_scheme(SunkenBoxScheme color_scheme) { 10 | switch (color_scheme) { 11 | case SunkenBoxSchemeSunkenBorders: 12 | top_color = color_dark_bg(); 13 | bottom_color = color_dark_bg_highlight(); 14 | borders_on = true; 15 | border_top_color = color_light_border(); 16 | border_right_color = color_light_border(); 17 | border_bottom_color = color_light_border(); 18 | border_left_color = color_light_border(); 19 | break; 20 | case SunkenBoxSchemeSunken: 21 | top_color = color_dark_bg(); 22 | bottom_color = color_dark_bg_highlight(); 23 | borders_on = false; 24 | break; 25 | case SunkenBoxSchemeRaised: 26 | top_color = color_dark_bg_highlight(); 27 | bottom_color = color_dark_bg(); 28 | borders_on = false; 29 | break; 30 | case SunkenBoxSchemeRaisedBorders: 31 | top_color = color_dark_bg_highlight(); 32 | bottom_color = color_dark_bg(); 33 | borders_on = true; 34 | border_top_color = color_light_border(); 35 | border_right_color = color_light_border(); 36 | border_bottom_color = color_light_border(); 37 | border_left_color = color_light_border(); 38 | break; 39 | case SunkenBoxSchemeInactive: 40 | top_color = color_dark_bg_inactive(); 41 | bottom_color = color_dark_bg_inactive(); 42 | borders_on = false; 43 | break; 44 | } 45 | } 46 | 47 | void SunkenBox::update(Widget *widget, int left, int top, int width, int height) { 48 | gradient_model = widget->transform2d(left, top, width, height); 49 | if (borders_on) { 50 | border_top_model = widget->transform2d(left, top, width, 1); 51 | border_bottom_model = widget->transform2d(left, top + height - 1, width, 1); 52 | border_left_model = widget->transform2d(left, top, 1, height); 53 | border_right_model = widget->transform2d(left + width, top, 1, height); 54 | } 55 | } 56 | 57 | void SunkenBox::draw(GuiWindow *gui_window, const glm::mat4 &projection) { 58 | gui_window->fill_rect_gradient(top_color, bottom_color, projection * gradient_model); 59 | if (borders_on) { 60 | gui_window->fill_rect(border_top_color, projection * border_top_model); 61 | gui_window->fill_rect(border_right_color, projection * border_right_model); 62 | gui_window->fill_rect(border_bottom_color, projection * border_bottom_model); 63 | gui_window->fill_rect(border_left_color, projection * border_left_model); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/thread_safe_queue_test.cpp: -------------------------------------------------------------------------------- 1 | #include "thread_safe_queue_test.hpp" 2 | #include "thread_safe_queue.hpp" 3 | #include "os.hpp" 4 | 5 | ThreadSafeQueue *queue = nullptr; 6 | 7 | static void test_assert(bool expr, const char *explain) { 8 | if (!expr) 9 | panic("assertion failure: %s", explain); 10 | } 11 | 12 | static void assert_no_err(int err) { 13 | if (err) 14 | panic("Error: %s", genesis_strerror(err)); 15 | } 16 | 17 | static void worker_thread_1(void *userdata) { 18 | queue->enqueue(13); 19 | } 20 | 21 | static void worker_thread_2(void *userdata) { 22 | queue->enqueue(17); 23 | } 24 | 25 | static void dequeue_no_assert(void *userdata) { 26 | queue->dequeue(); 27 | } 28 | 29 | static void dequeue_one_through_five(void *userdata) { 30 | for (int i = 0; i < 5; i += 1) { 31 | test_assert(queue->dequeue() == i, "dequeue one wrong value"); 32 | } 33 | } 34 | 35 | void test_thread_safe_queue(void) { 36 | queue = create>(); 37 | assert_no_err(queue->resize(20)); 38 | 39 | // basic test 40 | queue->enqueue(25); 41 | test_assert(queue->dequeue() == 25, "wrong dequeue value"); 42 | 43 | // let's get some threads going test 44 | OsThread *thread1; 45 | assert_no_err(os_thread_create(worker_thread_1, nullptr, false, &thread1)); 46 | 47 | OsThread *thread2; 48 | assert_no_err(os_thread_create(worker_thread_2, nullptr, false, &thread2)); 49 | 50 | int value_1 = queue->dequeue(); 51 | int value_2 = queue->dequeue(); 52 | if (value_1 + value_2 != 13 + 17) 53 | panic("wrong dequeue value. Got: %d and %d. Expected 13 and 17 (in any order).", value_1, value_2); 54 | 55 | OsThread *thread3; 56 | assert_no_err(os_thread_create(dequeue_no_assert, nullptr, false, &thread3)); 57 | 58 | queue->wakeup_all(); 59 | 60 | os_thread_destroy(thread1); 61 | os_thread_destroy(thread2); 62 | os_thread_destroy(thread3); 63 | 64 | 65 | // test wraparound 66 | assert_no_err(queue->resize(5)); 67 | assert_no_err(os_thread_create(dequeue_one_through_five, nullptr, false, &thread1)); 68 | for (int i = 0; i < 5; i += 1) { 69 | queue->enqueue(i); 70 | } 71 | os_thread_destroy(thread1); 72 | assert_no_err(os_thread_create(dequeue_one_through_five, nullptr, false, &thread1)); 73 | for (int i = 0; i < 5; i += 1) { 74 | queue->enqueue(i); 75 | } 76 | os_thread_destroy(thread1); 77 | 78 | // wakeup_all waking up a blocking reading thread 79 | assert_no_err(os_thread_create(dequeue_no_assert, nullptr, false, &thread1)); 80 | OsMutex *mutex = ok_mem(os_mutex_create()); 81 | OsCond *cond = ok_mem(os_cond_create()); 82 | os_cond_timed_wait(cond, mutex, 0.001); 83 | queue->wakeup_all(); 84 | os_thread_destroy(thread1); 85 | 86 | os_mutex_destroy(mutex); 87 | os_cond_destroy(cond); 88 | 89 | destroy(queue, 1); 90 | } 91 | -------------------------------------------------------------------------------- /src/audio_file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_FILE_HPP 2 | #define AUDIO_FILE_HPP 3 | 4 | #include "genesis.h" 5 | #include "list.hpp" 6 | #include "hash_map.hpp" 7 | #include "byte_buffer.hpp" 8 | #include "ffmpeg.hpp" 9 | 10 | struct Channel { 11 | List samples; 12 | }; 13 | 14 | struct GenesisAudioFile { 15 | List channels; 16 | SoundIoChannelLayout channel_layout; 17 | int sample_rate; 18 | HashMap tags; 19 | AVFormatContext *ic; 20 | AVCodecContext *codec_ctx; 21 | AVFrame *in_frame; 22 | GenesisContext *genesis_context; 23 | }; 24 | 25 | struct GenesisAudioFileStream { 26 | SoundIoChannelLayout channel_layout; 27 | int sample_rate; 28 | HashMap tags; 29 | GenesisExportFormat export_format; 30 | void (*write_frames)(const float *frames, int channel_count, 31 | int offset, int end, uint8_t *buffer, AVFrame *frame); 32 | FILE *file; 33 | AVIOContext *avio; 34 | AVFormatContext *fmt_ctx; 35 | unsigned char *avio_buf; 36 | uint8_t *frame_buffer; 37 | int frame_buffer_size; 38 | int buffer_frame_count; 39 | AVFrame *frame; 40 | AVStream *stream; 41 | AVPacket pkt; 42 | int pkt_offset; 43 | int avio_buffer_size; 44 | int bytes_per_frame; 45 | int bytes_per_sample; 46 | }; 47 | 48 | struct GenesisAudioFileCodec { 49 | GenesisAudioFileFormat *audio_file_format; 50 | GenesisRenderFormat *render_format; 51 | AVCodec *codec; 52 | List sample_format_list; 53 | List sample_rate_list; 54 | List bit_rate_list; 55 | bool has_bit_rate; 56 | }; 57 | 58 | // When modifying, see prioritized_render_formats in audio_file.cpp 59 | enum RenderFormatType { 60 | RenderFormatTypeInvalid = -1, 61 | 62 | RenderFormatTypeFlac = 0, 63 | RenderFormatTypeVorbis, 64 | RenderFormatTypeOpus, 65 | RenderFormatTypeWav, 66 | RenderFormatTypeMp3, 67 | RenderFormatTypeAac, 68 | 69 | RenderFormatTypeCount, 70 | }; 71 | 72 | struct GenesisAudioFileFormat { 73 | List codecs; 74 | AVInputFormat *iformat; 75 | }; 76 | 77 | struct GenesisRenderFormat { 78 | GenesisAudioFileCodec codec; 79 | AVOutputFormat *oformat; 80 | const char *description; 81 | const char *extension; 82 | RenderFormatType render_format_type; 83 | }; 84 | 85 | 86 | int audio_file_init(void); 87 | int __attribute__((warn_unused_result)) audio_file_get_out_formats(List &formats); 88 | int __attribute__((warn_unused_result)) audio_file_get_in_formats(List &formats); 89 | GenesisAudioFileCodec *audio_file_guess_audio_file_codec( 90 | List &out_formats, const char *filename_hint, 91 | const char *format_name, const char *codec_name); 92 | 93 | uint64_t channel_layout_to_libav(const SoundIoChannelLayout *channel_layout); 94 | 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/midi_hardware.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MIDI_CONTROLLER_HPP 2 | #define MIDI_CONTROLLER_HPP 3 | 4 | #include "list.hpp" 5 | #include "os.hpp" 6 | #include "genesis.h" 7 | #include "atomics.hpp" 8 | 9 | #include 10 | 11 | struct MidiHardware; 12 | 13 | enum GenesisMidiEventType { 14 | GenesisMidiEventTypeNoteOn, 15 | GenesisMidiEventTypeNoteOff, 16 | GenesisMidiEventTypePitch, 17 | GenesisMidiEventTypeSegment, 18 | }; 19 | 20 | struct MidiEventNoteData { 21 | float velocity; 22 | int note; 23 | }; 24 | 25 | struct MidiEventPitchData { 26 | float pitch; 27 | }; 28 | 29 | struct MidiEventSegmentData { 30 | long start; 31 | long end; 32 | }; 33 | 34 | struct GenesisMidiEvent { 35 | int event_type; 36 | double start; // in whole notes 37 | union { 38 | MidiEventNoteData note_data; 39 | MidiEventPitchData pitch_data; 40 | MidiEventSegmentData segment_data; 41 | } data; 42 | }; 43 | 44 | struct GenesisMidiDevice { 45 | MidiHardware *midi_hardware; 46 | int client_id; 47 | int port_id; 48 | char *client_name; 49 | char *port_name; 50 | 51 | int ref_count; 52 | bool open; 53 | int set_index; 54 | void (*on_event)(struct GenesisMidiDevice *, const struct GenesisMidiEvent *); 55 | void *userdata; 56 | }; 57 | 58 | struct MidiDevicesInfo { 59 | List devices; 60 | int default_device_index; 61 | }; 62 | 63 | struct MidiHardware { 64 | GenesisContext *context; 65 | snd_seq_t *seq; 66 | int client_id; 67 | 68 | // the one that the API user reads directly 69 | MidiDevicesInfo *current_devices_info; 70 | // created when changes are detected, queued up 71 | MidiDevicesInfo *ready_devices_info; 72 | GenesisMidiDevice *system_announce_device; 73 | GenesisMidiDevice *system_timer_device; 74 | 75 | OsThread *thread; 76 | atomic_bool quit_flag; 77 | OsMutex *mutex; 78 | 79 | void *userdata; 80 | void (*on_buffer_overrun)(struct MidiHardware *); 81 | void (*on_devices_change)(struct MidiHardware *); 82 | void (*events_signal)(struct MidiHardware *); 83 | 84 | List open_devices; 85 | 86 | // utility pointers, used only within a single function 87 | snd_seq_client_info_t *client_info; 88 | snd_seq_port_info_t *port_info; 89 | }; 90 | 91 | int create_midi_hardware(GenesisContext *context, 92 | const char *client_name, 93 | void (*events_signal)(struct MidiHardware *), 94 | void (*on_devices_change)(struct MidiHardware *), 95 | void *userdata, 96 | struct MidiHardware **out_midi_hardware); 97 | 98 | void destroy_midi_hardware(struct MidiHardware *midi_hardware); 99 | 100 | void midi_hardware_flush_events(MidiHardware *midi_hardware); 101 | 102 | int open_midi_device(struct GenesisMidiDevice *device); 103 | int close_midi_device(struct GenesisMidiDevice *device); 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/widget.cpp: -------------------------------------------------------------------------------- 1 | #include "widget.hpp" 2 | #include "gui_window.hpp" 3 | #include "gui.hpp" 4 | 5 | Widget::Widget(GuiWindow *gui_window) : 6 | gui_window(gui_window), 7 | parent_widget(nullptr), 8 | gui(gui_window->gui), 9 | left(0), 10 | top(0), 11 | width(100), 12 | height(100), 13 | is_visible(true) 14 | { 15 | layout_row = -1; 16 | } 17 | 18 | Widget::~Widget() { 19 | gui_window->remove_widget(this); 20 | if (parent_widget) 21 | parent_widget->remove_widget(this); 22 | } 23 | 24 | void Widget::remove_widget(Widget *widget) { 25 | panic("unimplemented"); 26 | } 27 | 28 | void Widget::on_size_hints_changed() { 29 | if (parent_widget) { 30 | parent_widget->on_resize(); 31 | } else { 32 | bool changed = false; 33 | int min_w = min_width(); 34 | int min_h = min_height(); 35 | int max_w = max_width(); 36 | int max_h = max_height(); 37 | if (width < min_w) { 38 | width = min_w; 39 | changed = true; 40 | } else if (width > max_w) { 41 | width = max_w; 42 | changed = true; 43 | } 44 | if (height < min_h) { 45 | height = min_h; 46 | changed = true; 47 | } else if (height > max_h) { 48 | height = max_h; 49 | changed = true; 50 | } 51 | if (changed) 52 | on_resize(); 53 | } 54 | } 55 | 56 | glm::mat4 Widget::transform2d(int rel_left, int rel_top, float scale_width, float scale_height) { 57 | return glm::scale( 58 | glm::translate( 59 | glm::mat4(1.0f), 60 | glm::vec3(left + rel_left, top + rel_top, 0.0f)), 61 | glm::vec3(scale_width, scale_height, 1.0f)); 62 | } 63 | 64 | glm::mat4 Widget::transform2d(int rel_left, int rel_top) { 65 | return glm::translate(glm::mat4(1.0f), 66 | glm::vec3(left + rel_left, top + rel_top, 0.0f)); 67 | } 68 | 69 | ContextMenuWidget *Widget::pop_context_menu(MenuWidgetItem *menu_widget_item, 70 | int rel_left, int rel_top, int box_width, int box_height) 71 | { 72 | return gui_window->pop_context_menu(menu_widget_item, left + rel_left, top + rel_top, box_width, box_height); 73 | } 74 | 75 | bool Widget::forward_drag_event(Widget *widget, const DragEvent *event) { 76 | DragEvent drag_event = *event; 77 | drag_event.orig_event.x += left; 78 | drag_event.orig_event.y += top; 79 | drag_event.mouse_event.x += left; 80 | drag_event.mouse_event.y += top; 81 | return gui_window->forward_drag_event(widget, &drag_event); 82 | } 83 | 84 | bool Widget::forward_mouse_event(Widget *widget, const MouseEvent *event) { 85 | MouseEvent mouse_event = *event; 86 | mouse_event.x += left; 87 | mouse_event.y += top; 88 | return gui_window->try_mouse_move_event_on_widget(widget, &mouse_event); 89 | } 90 | 91 | bool Widget::forward_mouse_wheel_event(Widget *widget, const MouseWheelEvent *event) { 92 | MouseWheelEvent mouse_wheel_event = *event; 93 | mouse_wheel_event.x += left; 94 | mouse_wheel_event.y += top; 95 | return gui_window->forward_mouse_wheel_event(widget, &mouse_wheel_event); 96 | } 97 | -------------------------------------------------------------------------------- /src/event_dispatcher.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVENT_DISPATCHER_HPP 2 | #define EVENT_DISPATCHER_HPP 3 | 4 | #include "util.hpp" 5 | 6 | enum Event { 7 | EventProjectUndoChanged, 8 | EventProjectUsersChanged, 9 | EventProjectTracksChanged, 10 | EventProjectCommandsChanged, 11 | EventWindowClose, 12 | EventWindowPosChange, 13 | EventWindowSizeChange, 14 | EventAudioDeviceChange, 15 | EventMidiDeviceChange, 16 | EventFlushEvents, 17 | EventPerspectiveChange, 18 | EventScrollValueChange, 19 | EventProjectAudioAssetsChanged, 20 | EventProjectAudioClipsChanged, 21 | EventProjectAudioClipSegmentsChanged, 22 | EventProjectMixerLinesChanged, 23 | EventProjectEffectsChanged, 24 | EventBufferUnderrun, 25 | EventSoundBackendDisconnected, 26 | EventDeviceDesignationChange, 27 | EventProjectSampleRateChanged, 28 | EventProjectChannelLayoutChanged, 29 | EventSelectedIndexChanged, 30 | EventActivate, 31 | EventSettingsDefaultRenderFormatChanged, 32 | EventSettingsDefaultRenderSampleFormatChanged, 33 | EventSettingsDefaultRenderBitRateChanged, 34 | EventRenderJobsUpdated, 35 | EventAudioGraphPlayHeadChanged, 36 | EventAudioGraphPlayingChanged, 37 | }; 38 | 39 | struct EventHandler { 40 | Event event; 41 | void (*fn)(Event, void *); 42 | void *userdata; 43 | }; 44 | 45 | class EventDispatcher { 46 | public: 47 | void attach_handler(Event event, void (*fn)(Event, void *), void *userdata) { 48 | ok_or_panic(event_handlers.add_one()); 49 | EventHandler *event_handler = &event_handlers.last(); 50 | event_handler->event = event; 51 | event_handler->fn = fn; 52 | event_handler->userdata = userdata; 53 | } 54 | 55 | void detach_handler(Event event, void (*fn)(Event, void *)) { 56 | for (int i = 0; i < event_handlers.length(); i += 1) { 57 | EventHandler *event_handler = &event_handlers.at(i); 58 | if (event_handler->event == event && event_handler->fn == fn) { 59 | event_handlers.swap_remove(i); 60 | return; 61 | } 62 | } 63 | panic("event handler not attached"); 64 | } 65 | 66 | void trigger(Event event) { 67 | // we use the stack here because we want to call this function in the main loop 68 | // need the memory on the stack because calling a handler might destroy this class 69 | static const int MAX_HANDLERS = 256; 70 | EventHandler * handlers[MAX_HANDLERS]; 71 | int handler_index = 0; 72 | 73 | for (int i = 0; i < event_handlers.length(); i += 1) { 74 | EventHandler *handler = &event_handlers.at(i); 75 | if (handler->event == event) { 76 | handlers[handler_index++] = handler; 77 | assert(handler_index <= MAX_HANDLERS); 78 | } 79 | } 80 | // we use a deferred list like this in case any event handlers 81 | // destroy this EventDispatcher 82 | for (int i = 0; i < handler_index; i += 1) { 83 | EventHandler *handler = handlers[i]; 84 | handler->fn(handler->event, handler->userdata); 85 | } 86 | } 87 | 88 | List event_handlers; 89 | 90 | }; 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /src/genesis_editor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_EDITOR 2 | #define GENESIS_EDITOR 3 | 4 | #include "key_event.hpp" 5 | #include "gui.hpp" 6 | 7 | class GuiWindow; 8 | struct GenesisContext; 9 | struct Project; 10 | struct SettingsFile; 11 | struct User; 12 | class MenuWidgetItem; 13 | class MenuWidget; 14 | class GenesisEditor; 15 | class DockAreaWidget; 16 | class TextWidget; 17 | struct SettingsFilePerspective; 18 | struct SettingsFileOpenWindow; 19 | struct SettingsFileDock; 20 | class DockablePaneWidget; 21 | struct EditorWindow; 22 | struct AudioGraph; 23 | 24 | struct EditorPane { 25 | DockablePaneWidget *pane; 26 | EditorWindow *editor_window; 27 | MenuWidgetItem *show_menu_item; 28 | }; 29 | 30 | struct EditorWindow { 31 | GenesisEditor *genesis_editor; 32 | GuiWindow *window; 33 | MenuWidgetItem *undo_menu; 34 | MenuWidgetItem *redo_menu; 35 | MenuWidgetItem *always_show_tabs_menu; 36 | MenuWidgetItem *show_view_menu; 37 | MenuWidgetItem *toggle_playback_menu; 38 | MenuWidgetItem *restart_playback_menu; 39 | MenuWidgetItem *stop_playback_menu; 40 | bool always_show_tabs; 41 | DockAreaWidget* dock_area; 42 | TextWidget *fps_widget; 43 | List all_panes; 44 | MenuWidget *menu_widget; 45 | }; 46 | 47 | class GenesisEditor { 48 | public: 49 | GenesisEditor(); 50 | ~GenesisEditor(); 51 | 52 | void exec(); 53 | void create_window(SettingsFileOpenWindow *sf_open_window); 54 | 55 | 56 | GenesisContext *genesis_context; 57 | 58 | ResourceBundle *resource_bundle; 59 | Gui *gui; 60 | 61 | List windows; 62 | Project *project; 63 | AudioGraph *audio_graph; 64 | User *user; 65 | SettingsFile *settings_file; 66 | 67 | int underrun_count; 68 | 69 | bool on_key_event(GuiWindow *window, const KeyEvent *event); 70 | bool on_text_event(GuiWindow *window, const TextInputEvent *event); 71 | 72 | void destroy_find_file_widget(); 73 | void destroy_audio_edit_widget(); 74 | 75 | void refresh_menu_state(); 76 | int window_index(EditorWindow *window); 77 | void close_window(EditorWindow *window); 78 | void close_others(EditorWindow *window); 79 | 80 | void do_undo(); 81 | void do_redo(); 82 | 83 | void load_perspective(EditorWindow *window, SettingsFilePerspective *perspective); 84 | void load_dock(EditorWindow *editor_window, DockAreaWidget *dock_area, SettingsFileDock *sf_dock); 85 | DockablePaneWidget *get_pane_widget(EditorWindow *editor_window, const String &title); 86 | void create_editor_window(); 87 | SettingsFileOpenWindow *create_sf_open_window(); 88 | void save_window_config(); 89 | void save_dock(DockAreaWidget *dock_area, SettingsFileDock *sf_dock); 90 | void add_dock(EditorWindow *editor_window, Widget *widget, const char *title); 91 | 92 | void show_view(EditorPane *editor_pane); 93 | DockablePaneWidget *find_pane(EditorPane *editor_pane, DockAreaWidget *dock_area); 94 | 95 | void toggle_playback(); 96 | void restart_playback(); 97 | void stop_playback(); 98 | 99 | GenesisEditor(const GenesisEditor ©) = delete; 100 | GenesisEditor &operator=(const GenesisEditor ©) = delete; 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /test/valgrind.supp: -------------------------------------------------------------------------------- 1 | { 2 | 3 | Memcheck:Leak 4 | match-leak-kinds: possible 5 | fun:malloc 6 | ... 7 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 8 | fun:snd_config_hook_load 9 | } 10 | { 11 | 12 | Memcheck:Leak 13 | match-leak-kinds: possible 14 | fun:calloc 15 | ... 16 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 17 | fun:snd_config_hook_load 18 | } 19 | { 20 | 21 | Memcheck:Leak 22 | match-leak-kinds: possible 23 | fun:malloc 24 | ... 25 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 26 | fun:snd_config_update_r 27 | } 28 | { 29 | 30 | Memcheck:Leak 31 | match-leak-kinds: possible 32 | fun:calloc 33 | ... 34 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 35 | fun:snd_config_update_r 36 | } 37 | { 38 | 39 | Memcheck:Leak 40 | match-leak-kinds: possible 41 | fun:malloc 42 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 43 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 44 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 45 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 46 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 47 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 48 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 49 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 50 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 51 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 52 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 53 | } 54 | { 55 | 56 | Memcheck:Leak 57 | match-leak-kinds: possible 58 | fun:calloc 59 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 60 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 61 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 62 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 63 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 64 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 65 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 66 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 67 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 68 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 69 | obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0 70 | } 71 | { 72 | 73 | Memcheck:Cond 74 | obj:/usr/lib/nvidia-346/libnvidia-glcore.so.346.59 75 | ... 76 | obj:/usr/lib/nvidia-346/libnvidia-glcore.so.346.59 77 | } 78 | { 79 | 80 | Memcheck:Leak 81 | match-leak-kinds: definite 82 | fun:malloc 83 | obj:/usr/lib/nvidia-346/libGL.so.346.59 84 | fun:glXQueryExtension 85 | obj:/usr/lib/x86_64-linux-gnu/libglfw.so.3.1 86 | obj:/usr/lib/x86_64-linux-gnu/libglfw.so.3.1 87 | fun:glfwInit 88 | } 89 | { 90 | 91 | Memcheck:Param 92 | writev(vector[...]) 93 | obj:*/libc-2.21.so 94 | obj:*/libxcb.so.1.1.0 95 | obj:*/libxcb.so.1.1.0 96 | fun:xcb_writev 97 | fun:_XSend 98 | fun:_XReply 99 | fun:XInternAtom 100 | fun:XSetWMProperties 101 | fun:Xutf8SetWMProperties 102 | obj:*/libglfw.so.3.1 103 | obj:*/libglfw.so.3.1 104 | obj:*/libglfw.so.3.1 105 | } 106 | -------------------------------------------------------------------------------- /src/shader_program.cpp: -------------------------------------------------------------------------------- 1 | #include "shader_program.hpp" 2 | #include "byte_buffer.hpp" 3 | 4 | static void init_shader(const char *source, const char *name, GLenum type, GLuint &shader_id) { 5 | shader_id = glCreateShader(type); 6 | glShaderSource(shader_id, 1, &source, NULL); 7 | glCompileShader(shader_id); 8 | 9 | GLint ok; 10 | glGetShaderiv(shader_id, GL_COMPILE_STATUS, &ok); 11 | if (ok) 12 | return; 13 | 14 | GLint error_size; 15 | glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &error_size); 16 | 17 | ByteBuffer error_string; 18 | error_string.resize(error_size); 19 | glGetShaderInfoLog(shader_id, error_size, &error_size, error_string.raw()); 20 | panic("Error compiling %s shader:\n%s", name, error_string.raw()); 21 | } 22 | 23 | ShaderProgram::ShaderProgram(const char *vertex_shader_source, 24 | const char *fragment_shader_source, 25 | const char *geometry_shader_source) 26 | { 27 | init_shader(vertex_shader_source, "vertex", GL_VERTEX_SHADER, vertex_id); 28 | init_shader(fragment_shader_source, "fragment", GL_FRAGMENT_SHADER, fragment_id); 29 | if (geometry_shader_source) { 30 | init_shader(geometry_shader_source, "geometry", GL_GEOMETRY_SHADER, geometry_id); 31 | have_geometry = true; 32 | } else { 33 | have_geometry = false; 34 | } 35 | 36 | program_id = glCreateProgram(); 37 | glAttachShader(program_id, vertex_id); 38 | glAttachShader(program_id, fragment_id); 39 | if (geometry_shader_source) 40 | glAttachShader(program_id, geometry_id); 41 | glLinkProgram(program_id); 42 | 43 | GLint ok; 44 | glGetProgramiv(program_id, GL_LINK_STATUS, &ok); 45 | 46 | if (ok) 47 | return; 48 | 49 | GLint error_size; 50 | glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &error_size); 51 | 52 | ByteBuffer error_string; 53 | error_string.resize(error_size); 54 | glGetProgramInfoLog(program_id, error_size, &error_size, error_string.raw()); 55 | panic("Error linking shader program: %s", error_string.raw()); 56 | } 57 | 58 | ShaderProgram::~ShaderProgram() { 59 | if (have_geometry) 60 | glDetachShader(program_id, geometry_id); 61 | glDetachShader(program_id, fragment_id); 62 | glDetachShader(program_id, vertex_id); 63 | 64 | if (have_geometry) 65 | glDeleteShader(geometry_id); 66 | glDeleteShader(fragment_id); 67 | glDeleteShader(vertex_id); 68 | 69 | glDeleteProgram(program_id); 70 | } 71 | 72 | void ShaderProgram::set_uniform(GLint uniform_id, int value) const 73 | { 74 | glUniform1i(uniform_id, value); 75 | } 76 | 77 | void ShaderProgram::set_uniform(GLint uniform_id, float value) const 78 | { 79 | glUniform1f(uniform_id, value); 80 | } 81 | 82 | void ShaderProgram::set_uniform(GLint uniform_id, const glm::vec3 &value) const 83 | { 84 | glUniform3fv(uniform_id, 1, &value[0]); 85 | } 86 | 87 | void ShaderProgram::set_uniform(GLint uniform_id, const glm::vec4 &value) const 88 | { 89 | glUniform4fv(uniform_id, 1, &value[0]); 90 | } 91 | 92 | void ShaderProgram::set_uniform(GLint uniform_id, const glm::mat4 &value) const 93 | { 94 | glUniformMatrix4fv(uniform_id, 1, GL_FALSE, &value[0][0]); 95 | } 96 | 97 | void ShaderProgram::set_uniform(GLint uniform_id, const glm::mat3 &value) const 98 | { 99 | glUniformMatrix3fv(uniform_id, 1, GL_FALSE, &value[0][0]); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/audio_graph.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_GRAPH_HPP 2 | #define AUDIO_GRAPH_HPP 3 | 4 | #include "project.hpp" 5 | #include "atomic_value.hpp" 6 | #include "atomic_double.hpp" 7 | #include "atomics.hpp" 8 | #include "midi_hardware.hpp" 9 | #include "settings_file.hpp" 10 | #include "event_dispatcher.hpp" 11 | 12 | struct EventList { 13 | List events; 14 | }; 15 | 16 | struct AudioGraph; 17 | 18 | struct AudioGraphClip { 19 | AudioGraph *audio_graph; 20 | AudioClip *audio_clip; 21 | GenesisNodeDescriptor *node_descr; 22 | GenesisNode *node; 23 | GenesisNodeDescriptor *event_node_descr; 24 | GenesisNode *event_node; 25 | GenesisNode *resample_node; 26 | AtomicValue> events; 27 | List *events_write_ptr; 28 | }; 29 | 30 | struct AudioGraph { 31 | Project *project; 32 | EventDispatcher events; 33 | 34 | List audio_clip_list; 35 | 36 | GenesisPipeline *pipeline; 37 | SettingsFile *settings_file; 38 | GenesisNodeDescriptor *resample_descr; 39 | GenesisNodeDescriptor *mixer_descr; 40 | GenesisNode *resample_node; 41 | GenesisNode *mixer_node; 42 | GenesisNode *master_node; 43 | 44 | long audio_file_frame_count; 45 | long audio_file_frame_index; 46 | PlayChannelContext audio_file_channel_context[GENESIS_MAX_CHANNELS]; 47 | 48 | GenesisPortDescriptor *audio_file_port_descr; 49 | GenesisNodeDescriptor *audio_file_descr; 50 | GenesisNode *audio_file_node; 51 | GenesisAudioFile *preview_audio_file; 52 | bool preview_audio_file_is_asset; 53 | 54 | GenesisNodeDescriptor *render_descr; 55 | GenesisPortDescriptor *render_port_descr; 56 | ByteBuffer render_out_path; 57 | GenesisExportFormat render_export_format; 58 | GenesisAudioFileStream *render_stream; 59 | atomic_long render_frame_index; 60 | long render_frame_count; 61 | OsCond *render_cond; 62 | 63 | double start_play_head_pos; 64 | double play_head_pos; 65 | atomic_bool is_playing; 66 | atomic_flag play_head_changed_flag; 67 | }; 68 | 69 | int audio_graph_create_playback(Project *project, GenesisContext *genesis_context, 70 | SettingsFile *settings_file, AudioGraph **out_audio_graph); 71 | int audio_graph_create_render(Project *project, GenesisContext *genesis_context, 72 | const GenesisExportFormat *export_format, const ByteBuffer &out_path, 73 | AudioGraph **out_audio_graph); 74 | void audio_graph_destroy(AudioGraph *audio_graph); 75 | 76 | void audio_graph_start_pipeline(AudioGraph *audio_graph); 77 | 78 | double audio_graph_get_latency(AudioGraph *audio_graph); 79 | 80 | void audio_graph_play_sample_file(AudioGraph *audio_graph, const ByteBuffer &path); 81 | void audio_graph_play_audio_asset(AudioGraph *audio_graph, AudioAsset *audio_asset); 82 | 83 | void audio_graph_set_play_head(AudioGraph *audio_graph, double pos); 84 | 85 | bool audio_graph_is_playing(AudioGraph *audio_graph); 86 | void audio_graph_pause(AudioGraph *audio_graph); 87 | void audio_graph_play(AudioGraph *audio_graph); 88 | void audio_graph_restart_playback(AudioGraph *audio_graph); 89 | void audio_graph_stop_playback(AudioGraph *audio_graph); 90 | 91 | void audio_graph_recover_stream(AudioGraph *audio_graph, double new_latency); 92 | void audio_graph_recover_sound_backend_disconnect(AudioGraph *audio_graph); 93 | void audio_graph_change_sample_rate(AudioGraph *audio_graph, int new_sample_rate); 94 | 95 | void audio_graph_flush_events(AudioGraph *audio_graph); 96 | double audio_graph_play_head_pos(AudioGraph *audio_graph); 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/dockable_pane_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DOCKABLE_PANE_WIDGET 2 | #define DOCKABLE_PANE_WIDGET 3 | 4 | #include "widget.hpp" 5 | 6 | #include 7 | 8 | class DockablePaneWidget; 9 | class TabWidget; 10 | class Label; 11 | struct SpritesheetImage; 12 | 13 | struct DockablePaneTab { 14 | DockablePaneWidget *pane; 15 | }; 16 | 17 | enum DockAreaLayout { 18 | DockAreaLayoutTabs, 19 | DockAreaLayoutHoriz, 20 | DockAreaLayoutVert, 21 | }; 22 | 23 | class DockAreaWidget : public Widget { 24 | public: 25 | DockAreaWidget(GuiWindow *window); 26 | ~DockAreaWidget() override; 27 | 28 | void draw(const glm::mat4 &projection) override; 29 | void on_mouse_move(const MouseEvent *event) override; 30 | void on_drag(const DragEvent *event) override; 31 | void on_resize() override { update_model(); } 32 | 33 | void add_left_pane(DockablePaneWidget *pane); 34 | void add_right_pane(DockablePaneWidget *pane); 35 | void add_top_pane(DockablePaneWidget *pane); 36 | void add_bottom_pane(DockablePaneWidget *pane); 37 | void add_tab_pane(DockablePaneWidget *pane); 38 | 39 | 40 | void add_left_dock_area(DockAreaWidget *dock_area); 41 | void add_right_dock_area(DockAreaWidget *dock_area); 42 | void add_top_dock_area(DockAreaWidget *dock_area); 43 | void add_bottom_dock_area(DockAreaWidget *dock_area); 44 | 45 | void set_auto_hide_tabs(bool value); 46 | 47 | void collapse(); 48 | void remove_a(); 49 | void remove_b(); 50 | 51 | // destroys all children and resets state 52 | void reset_state(); 53 | 54 | DockAreaLayout layout; 55 | DockAreaWidget *child_a; // left/top 56 | DockAreaWidget *child_b; // right/bottom 57 | TabWidget *tab_widget; // if layout == DockAreaLayoutTabs 58 | float split_ratio; 59 | int split_area_size; // width/height of the split ui 60 | glm::mat4 split_border_start_model; 61 | glm::mat4 split_border_end_model; 62 | glm::vec4 light_border_color; 63 | glm::vec4 dark_border_color; 64 | bool resize_down; 65 | int resize_down_pos; 66 | float resize_down_ratio; 67 | bool auto_hide_tabs; 68 | glm::mat4 insert_arrow_model; 69 | bool insert_tab_arrow; 70 | glm::vec4 insert_tab_arrow_color; 71 | 72 | bool show_drop_lines; 73 | int center_drop_left; 74 | int center_drop_top; 75 | int center_drop_width; 76 | int center_drop_height; 77 | int center_drop_right; 78 | int center_drop_bottom; 79 | glm::mat4 drop_lines[4]; 80 | Label *drop_area_labels[5]; 81 | glm::mat4 drop_area_label_models[5]; 82 | const SpritesheetImage *drop_area_icon; 83 | glm::mat4 drop_area_icon_model; 84 | 85 | DockAreaWidget * transfer_state_to_new_child(); 86 | void transfer_state(DockAreaWidget *source, DockAreaWidget *dest); 87 | void add_tab_widget(DockablePaneWidget *pane); 88 | DockAreaWidget *create_dock_area_for_pane(DockablePaneWidget *pane); 89 | void update_model(); 90 | void handle_tab_drag(const DragEvent *event); 91 | void clear_drag_ui(); 92 | }; 93 | 94 | class DockablePaneWidget : public Widget { 95 | public: 96 | DockablePaneWidget(Widget *child, const String &title); 97 | ~DockablePaneWidget() override {} 98 | 99 | void draw(const glm::mat4 &projection) override; 100 | void on_mouse_move(const MouseEvent *event) override; 101 | void on_drag(const DragEvent *event) override; 102 | void on_resize() override; 103 | 104 | Widget *child; 105 | String title; 106 | }; 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/locked_queue.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LOCKED_QUEUE_HPP 2 | #define LOCKED_QUEUE_HPP 3 | 4 | #include "os.hpp" 5 | #include "util.hpp" 6 | 7 | template 8 | class LockedQueue { 9 | public: 10 | LockedQueue() { 11 | _start = 0; 12 | _end = 0; 13 | _length = 0; 14 | _capacity = 0; 15 | _items = NULL; 16 | _shutdown = false; 17 | if (!(_mutex = os_mutex_create())) 18 | mutex_err = GenesisErrorNoMem; 19 | if (!(_cond = os_cond_create())) 20 | cond_err = GenesisErrorNoMem; 21 | } 22 | 23 | ~LockedQueue() { 24 | destroy(_items, _capacity); 25 | os_mutex_destroy(_mutex); 26 | os_cond_destroy(_cond); 27 | } 28 | 29 | int error() { 30 | if (mutex_err) 31 | return mutex_err; 32 | if (cond_err) 33 | return cond_err; 34 | return 0; 35 | } 36 | 37 | // returns 0 on success or GenesisErrorNoMem 38 | int __attribute__((warn_unused_result)) push(T item) { 39 | OsMutexLocker locker(_mutex); 40 | 41 | int err = ensure_capacity(_length + 1); 42 | if (err) 43 | return err; 44 | 45 | _length += 1; 46 | _items[_end] = item; 47 | _end = (_end + 1) % _capacity; 48 | os_cond_signal(_cond, _mutex); 49 | return 0; 50 | } 51 | 52 | // returns 0 on success or GenesisErrorAborted 53 | int shift(T *result) { 54 | OsMutexLocker locker(_mutex); 55 | 56 | for (;;) { 57 | if (_shutdown) 58 | return GenesisErrorAborted; 59 | if (_length <= 0) { 60 | os_cond_wait(_cond, _mutex); 61 | continue; 62 | } 63 | _length -= 1; 64 | *result = _items[_start]; 65 | _start = (_start + 1) % _capacity; 66 | return 0; 67 | } 68 | } 69 | 70 | void wakeup_all() { 71 | OsMutexLocker locker(_mutex); 72 | _shutdown = true; 73 | os_cond_signal(_cond, _mutex); 74 | } 75 | 76 | int length() { 77 | OsMutexLocker locker(_mutex); 78 | return _length; 79 | } 80 | 81 | private: 82 | T * _items; 83 | int _start; 84 | int _end; 85 | int _length; 86 | int _capacity; 87 | OsMutex *_mutex; 88 | int mutex_err; 89 | OsCond *_cond; 90 | int cond_err; 91 | bool _shutdown; 92 | 93 | int ensure_capacity(int new_capacity) { 94 | int better_capacity = max(_capacity, 16); 95 | while (better_capacity < new_capacity) 96 | better_capacity = better_capacity * 2; 97 | if (better_capacity == _capacity) 98 | return 0; 99 | 100 | T *new_items = reallocate_safe(_items, _capacity, better_capacity); 101 | if (!new_items) 102 | return GenesisErrorNoMem; 103 | _items = new_items; 104 | 105 | int start_until_capacity = _capacity - _start; 106 | for (int i = start_until_capacity; i < _length; i += 1) { 107 | int old_index = (_start + i) % _capacity; 108 | int new_index = (_start + i) % better_capacity; 109 | _items[new_index] = _items[old_index]; 110 | } 111 | _end = (_start + _length) % better_capacity; 112 | 113 | _capacity = better_capacity; 114 | return 0; 115 | } 116 | 117 | LockedQueue(const LockedQueue &other) = delete; 118 | LockedQueue& operator= (const LockedQueue &other) = delete; 119 | }; 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /src/midi_note_pitch.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MIDI_NOTE_PITCH_HPP 2 | #define MIDI_NOTE_PITCH_HPP 3 | 4 | static const float midi_note_to_pitch[] = { 5 | 8.175798915643707, 6 | 8.661957218027252, 7 | 9.177023997418988, 8 | 9.722718241315029, 9 | 10.300861153527183, 10 | 10.913382232281373, 11 | 11.562325709738575, 12 | 12.249857374429663, 13 | 12.978271799373287, 14 | 13.75, 15 | 14.567617547440307, 16 | 15.433853164253883, 17 | 16.351597831287414, 18 | 17.323914436054505, 19 | 18.354047994837977, 20 | 19.445436482630058, 21 | 20.601722307054366, 22 | 21.826764464562746, 23 | 23.12465141947715, 24 | 24.499714748859326, 25 | 25.956543598746574, 26 | 27.5, 27 | 29.13523509488062, 28 | 30.86770632850775, 29 | 32.70319566257483, 30 | 34.64782887210901, 31 | 36.70809598967594, 32 | 38.890872965260115, 33 | 41.20344461410875, 34 | 43.653528929125486, 35 | 46.2493028389543, 36 | 48.999429497718666, 37 | 51.91308719749314, 38 | 55.0, 39 | 58.27047018976124, 40 | 61.7354126570155, 41 | 65.40639132514966, 42 | 69.29565774421802, 43 | 73.41619197935188, 44 | 77.78174593052023, 45 | 82.4068892282175, 46 | 87.30705785825097, 47 | 92.4986056779086, 48 | 97.99885899543733, 49 | 103.82617439498628, 50 | 110.0, 51 | 116.54094037952248, 52 | 123.47082531403103, 53 | 130.8127826502993, 54 | 138.59131548843604, 55 | 146.8323839587038, 56 | 155.56349186104046, 57 | 164.81377845643496, 58 | 174.61411571650194, 59 | 184.9972113558172, 60 | 195.99771799087463, 61 | 207.65234878997256, 62 | 220.0, 63 | 233.08188075904496, 64 | 246.94165062806206, 65 | 261.6255653005986, 66 | 277.1826309768721, 67 | 293.6647679174076, 68 | 311.1269837220809, 69 | 329.6275569128699, 70 | 349.2282314330039, 71 | 369.9944227116344, 72 | 391.99543598174927, 73 | 415.3046975799451, 74 | 440.0, 75 | 466.1637615180899, 76 | 493.8833012561241, 77 | 523.2511306011972, 78 | 554.3652619537442, 79 | 587.3295358348151, 80 | 622.2539674441618, 81 | 659.2551138257398, 82 | 698.4564628660078, 83 | 739.9888454232688, 84 | 783.9908719634985, 85 | 830.6093951598903, 86 | 880.0, 87 | 932.3275230361799, 88 | 987.7666025122483, 89 | 1046.5022612023945, 90 | 1108.7305239074883, 91 | 1174.6590716696303, 92 | 1244.5079348883237, 93 | 1318.5102276514797, 94 | 1396.9129257320155, 95 | 1479.9776908465376, 96 | 1567.981743926997, 97 | 1661.2187903197805, 98 | 1760.0, 99 | 1864.6550460723597, 100 | 1975.533205024496, 101 | 2093.004522404789, 102 | 2217.4610478149766, 103 | 2349.31814333926, 104 | 2489.0158697766474, 105 | 2637.02045530296, 106 | 2793.825851464031, 107 | 2959.955381693075, 108 | 3135.9634878539946, 109 | 3322.437580639561, 110 | 3520.0, 111 | 3729.3100921447194, 112 | 3951.066410048992, 113 | 4186.009044809578, 114 | 4434.922095629953, 115 | 4698.63628667852, 116 | 4978.031739553295, 117 | 5274.04091060592, 118 | 5587.651702928062, 119 | 5919.91076338615, 120 | 6271.926975707989, 121 | 6644.875161279122, 122 | 7040.0, 123 | 7458.620184289437, 124 | 7902.132820097988, 125 | 8372.018089619156, 126 | 8869.844191259906, 127 | 9397.272573357044, 128 | 9956.06347910659, 129 | 10548.081821211836, 130 | 11175.303405856126, 131 | 11839.8215267723, 132 | 12543.853951415975, 133 | }; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /src/generate_unicode_data.cpp: -------------------------------------------------------------------------------- 1 | #include "util.hpp" 2 | #include "byte_buffer.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static const uint32_t whitespace[] = {9, 10, 11, 12, 13, 32, 133, 160, 5760, 12 | 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8232, 13 | 8233, 8239, 8287, 12288}; 14 | 15 | struct UnicodeCharacter { 16 | uint32_t lower; 17 | uint32_t upper; 18 | }; 19 | 20 | int main(int argc, char *argv[]) { 21 | char *unicode_data_text_path = argv[1]; 22 | char *out_header_path = argv[2]; 23 | 24 | FILE *out = fopen(out_header_path, "w"); 25 | if (!out) 26 | panic("unable to write open %s", out_header_path); 27 | 28 | int fd = open(unicode_data_text_path, O_RDONLY); 29 | if (fd == -1) 30 | panic("unable to read open %s", unicode_data_text_path); 31 | 32 | struct stat st; 33 | if (fstat(fd, &st)) 34 | panic("fstat failed"); 35 | 36 | long file_size = st.st_size; 37 | 38 | FILE *f = fdopen(fd, "r"); 39 | 40 | if (!f) 41 | panic("unable to open fd"); 42 | 43 | ByteBuffer buffer; 44 | buffer.resize(file_size); 45 | 46 | long amt_read = fread(buffer.raw(), 1, file_size, f); 47 | if (amt_read != file_size) 48 | panic("unable to read"); 49 | 50 | if (fclose(f)) 51 | panic("unable to fclose in"); 52 | 53 | List lines; 54 | buffer.split("\n", lines); 55 | 56 | uint32_t max = 0; 57 | List case_info_list; 58 | 59 | for (uint32_t i = 0; i < (uint32_t)lines.length(); i += 1) { 60 | List line_fields; 61 | lines.at(i).split(";", line_fields); 62 | if (line_fields.length() < 15) 63 | break; 64 | 65 | uint32_t codepoint; 66 | sscanf(line_fields.at(0).raw(), "%X", &codepoint); 67 | 68 | while (codepoint > (uint32_t)case_info_list.length()) { 69 | ok_or_panic(case_info_list.append({ 0, 0 })); 70 | } 71 | 72 | ByteBuffer upper_str = line_fields.at(12); 73 | ByteBuffer lower_str = line_fields.at(13); 74 | uint32_t lower = codepoint; 75 | uint32_t upper = codepoint; 76 | if (lower_str.length() > 0 || upper_str.length() > 0) { 77 | max = codepoint; 78 | sscanf(lower_str.raw(), "%X", &lower); 79 | sscanf(upper_str.raw(), "%X", &upper); 80 | } 81 | ok_or_panic(case_info_list.append({ lower, upper })); 82 | } 83 | 84 | fprintf(out, "// This file is auto-generated.\n"); 85 | fprintf(out, "#ifndef UNICODE_HPP\n"); 86 | fprintf(out, "#define UNICODE_HPP\n"); 87 | fprintf(out, "#include \n"); 88 | 89 | fprintf(out, "static const uint32_t whitespace[] = {\n"); 90 | for (long i = 0; i < array_length(whitespace); i += 1) { 91 | fprintf(out, " 0x%x,\n", whitespace[i]); 92 | } 93 | fprintf(out, "};\n"); 94 | 95 | fprintf(out, "struct UnicodeCharacter {\n"); 96 | fprintf(out, " uint32_t lower;\n"); 97 | fprintf(out, " uint32_t upper;\n"); 98 | fprintf(out, "};\n"); 99 | fprintf(out, "static const UnicodeCharacter unicode_characters[] = {\n"); 100 | 101 | for (uint32_t i = 0; i < max; i += 1) { 102 | UnicodeCharacter *unicode_char = &case_info_list.at(i); 103 | fprintf(out, " {0x%x, 0x%x},\n", unicode_char->lower, unicode_char->upper); 104 | } 105 | 106 | fprintf(out, "};\n"); 107 | fprintf(out, "#endif\n"); 108 | 109 | if (fclose(out)) 110 | panic("unable to fclose out"); 111 | 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /src/crc32.cpp: -------------------------------------------------------------------------------- 1 | #include "crc32.hpp" 2 | 3 | static const uint32_t crc_table[256] = { 4 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 5 | 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 6 | 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 7 | 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 8 | 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 9 | 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 10 | 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 11 | 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 12 | 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 13 | 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 14 | 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 15 | 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 16 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 17 | 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 18 | 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 19 | 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 20 | 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 21 | 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 22 | 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 23 | 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 24 | 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 25 | 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 26 | 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 27 | 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 28 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 29 | 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 30 | 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 31 | 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 32 | 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 33 | 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 34 | 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 35 | 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 36 | 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 37 | 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 38 | 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 39 | 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 40 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 41 | 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 42 | 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 43 | 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 44 | 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 45 | 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 46 | 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 47 | 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 48 | 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 49 | 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 50 | 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 51 | 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 52 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 53 | 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 54 | 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 55 | 0x2d02ef8d 56 | }; 57 | 58 | uint32_t crc32(uint32_t crc, const unsigned char *buf, int len) { 59 | if (!buf) 60 | return 0; 61 | 62 | crc = crc ^ 0xffffffff; 63 | 64 | while (len--) 65 | crc = crc_table[(crc ^ (*buf++)) & 0xff] ^ (crc >> 8); 66 | 67 | return crc ^ 0xffffffff; 68 | } 69 | -------------------------------------------------------------------------------- /example/list_devices.c: -------------------------------------------------------------------------------- 1 | #include "genesis.h" 2 | 3 | #include 4 | #include 5 | 6 | // list or keep a watch on audio and midi devices 7 | 8 | static int usage(char *exe) { 9 | fprintf(stderr, "Usage: %s [--watch]\n", exe); 10 | return 1; 11 | } 12 | 13 | static int list_devices(struct GenesisContext *context) { 14 | genesis_flush_events(context); 15 | genesis_refresh_midi_devices(context); 16 | 17 | struct GenesisSoundBackend *sound_backend = genesis_default_backend(context); 18 | int input_count = genesis_input_device_count(sound_backend); 19 | int output_count = genesis_output_device_count(sound_backend); 20 | 21 | int midi_count = genesis_get_midi_device_count(context); 22 | if (midi_count < 0) { 23 | fprintf(stderr, "unable to find midi devices\n"); 24 | return 1; 25 | } 26 | 27 | int default_playback = genesis_default_output_device_index(sound_backend); 28 | int default_recording = genesis_default_input_device_index(sound_backend); 29 | int default_midi = genesis_get_default_midi_device_index(context); 30 | for (int i = 0; i < input_count; i += 1) { 31 | struct SoundIoDevice *device = genesis_get_input_device(sound_backend, i); 32 | const char *purpose_str = "recording"; 33 | const char *default_str = (i == default_recording) ? " (default)" : ""; 34 | int sample_rate = soundio_device_nearest_sample_rate(device, 44100); 35 | fprintf(stderr, "%s device: %d Hz %s%s\n", purpose_str, sample_rate, device->name, default_str); 36 | soundio_device_unref(device); 37 | } 38 | for (int i = 0; i < output_count; i += 1) { 39 | struct SoundIoDevice *device = genesis_get_output_device(sound_backend, i); 40 | const char *purpose_str = "playback"; 41 | const char *default_str = (i == default_playback) ? " (default)" : ""; 42 | int sample_rate = soundio_device_nearest_sample_rate(device, 44100); 43 | fprintf(stderr, "%s device: %d Hz %s%s\n", purpose_str, sample_rate, device->name, default_str); 44 | soundio_device_unref(device); 45 | } 46 | for (int i = 0; i < midi_count; i += 1) { 47 | struct GenesisMidiDevice *device = genesis_get_midi_device(context, i); 48 | const char *default_str = (i == default_midi) ? " (default)" : ""; 49 | const char *description = genesis_midi_device_description(device); 50 | fprintf(stderr, "controller device: %s%s\n", description, default_str); 51 | genesis_midi_device_unref(device); 52 | } 53 | fprintf(stderr, "%d devices found\n", input_count + output_count + midi_count); 54 | return 0; 55 | } 56 | 57 | static void on_devices_change(void *userdata) { 58 | struct GenesisContext *context = userdata; 59 | fprintf(stderr, "devices changed\n"); 60 | list_devices(context); 61 | } 62 | 63 | int main(int argc, char **argv) { 64 | char *exe = argv[0]; 65 | bool watch = false; 66 | 67 | for (int i = 1; i < argc; i += 1) { 68 | char *arg = argv[i]; 69 | if (strcmp("--watch", arg) == 0) { 70 | watch = true; 71 | } else { 72 | return usage(exe); 73 | } 74 | } 75 | 76 | struct GenesisContext *context; 77 | int err = genesis_context_create(&context); 78 | if (err) { 79 | fprintf(stderr, "unable to create context: %s\n", genesis_strerror(err)); 80 | return 1; 81 | } 82 | 83 | if (watch) { 84 | genesis_set_audio_device_callback(context, on_devices_change, context); 85 | genesis_set_midi_device_callback(context, on_devices_change, context); 86 | for (;;) { 87 | genesis_wait_events(context); 88 | } 89 | } else { 90 | int err = list_devices(context); 91 | genesis_context_destroy(context); 92 | return err; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/uint256.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UINT256_HPP 2 | #define UINT256_HPP 3 | 4 | #include "os.hpp" 5 | #include "hash_map.hpp" 6 | 7 | #include 8 | 9 | template 10 | class UIntOversized { 11 | public: 12 | uint64_t values[Size64]; 13 | 14 | static inline UIntOversized zero() { 15 | UIntOversized result; 16 | for (int i = 0; i < Size64; i += 1) 17 | result.values[i] = 0; 18 | return result; 19 | } 20 | 21 | static inline uint32_t hash(const UIntOversized & a) { 22 | // it's just a bunch of xor 23 | uint64_t result = 0; 24 | for (int i = 0; i < Size64; i += 1) 25 | result ^= a.values[i]; 26 | return (uint32_t)(result >> 32) ^ (uint32_t)(result & 0x00000000ffffffffULL); 27 | } 28 | 29 | static inline int compare(const UIntOversized &a, const UIntOversized &b) { 30 | for (int i = 0; i < Size64; i += 1) { 31 | if (a.values[i] > b.values[i]) 32 | return 1; 33 | else if (a.values[i] < b.values[i]) 34 | return -1; 35 | } 36 | return 0; 37 | } 38 | 39 | static inline UIntOversized random() { 40 | UIntOversized result; 41 | for (int i = 0; i < Size64; i += 1) 42 | result.values[i] = ((uint64_t)os_random_uint32()) << 32 | (uint64_t)os_random_uint32(); 43 | return result; 44 | } 45 | 46 | ByteBuffer to_string() const { 47 | ByteBuffer digit; 48 | ByteBuffer result; 49 | for (int i = 0; i < Size64; i += 1) { 50 | digit.format("%016" PRIx64, values[i]); 51 | result.append(digit); 52 | } 53 | return result; 54 | } 55 | 56 | static inline UIntOversized parse(const ByteBuffer &str) { 57 | UIntOversized result; 58 | if (str.length() != Size64 * 16) 59 | return zero(); 60 | for (int i = 0; i < Size64; i += 1) { 61 | sscanf(str.raw() + i * 16, "%016" SCNx64, &result.values[i]); 62 | } 63 | return result; 64 | } 65 | 66 | static inline UIntOversized read_be(const char *ptr) { 67 | const uint8_t *buffer = (const uint8_t*) ptr; 68 | UIntOversized result; 69 | 70 | for (int i = 0; i < Size64; i += 1) { 71 | result.values[i] = *buffer; 72 | buffer += 1; 73 | 74 | for (int byte = 1; byte < 8; byte += 1) { 75 | result.values[i] <<= 8; 76 | result.values[i] |= *buffer; 77 | buffer += 1; 78 | } 79 | } 80 | return result; 81 | } 82 | 83 | inline void write_be(char *ptr) const { 84 | uint8_t *buffer = (uint8_t *) ptr; 85 | for (int i = 0; i < Size64; i += 1) { 86 | uint64_t x = values[i]; 87 | buffer[7] = x & 0xff; x >>= 8; 88 | buffer[6] = x & 0xff; x >>= 8; 89 | buffer[5] = x & 0xff; x >>= 8; 90 | buffer[4] = x & 0xff; x >>= 8; 91 | 92 | buffer[3] = x & 0xff; x >>= 8; 93 | buffer[2] = x & 0xff; x >>= 8; 94 | buffer[1] = x & 0xff; x >>= 8; 95 | buffer[0] = x & 0xff; 96 | 97 | buffer += 8; 98 | } 99 | } 100 | }; 101 | 102 | template 103 | static inline bool operator==(const UIntOversized & a, const UIntOversized & b) { 104 | for (int i = 0; i < Size64; i += 1) 105 | if (a.values[i] != b.values[i]) 106 | return false; 107 | return true; 108 | } 109 | template 110 | static inline bool operator!=(const UIntOversized & a, const UIntOversized & b) { 111 | return !(a == b); 112 | } 113 | 114 | typedef UIntOversized<4> uint256; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /test/ring_buffer_test.cpp: -------------------------------------------------------------------------------- 1 | #include "ring_buffer_test.hpp" 2 | #include "ring_buffer.hpp" 3 | #include "os.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | using std::atomic_int; 11 | using std::atomic_bool; 12 | 13 | static void assert_no_err(int err) { 14 | if (err) 15 | panic("Error: %s", genesis_strerror(err)); 16 | } 17 | 18 | static void basic_test(void) { 19 | RingBuffer rb; 20 | assert_no_err(ring_buffer_init(&rb, 10)); 21 | 22 | assert(rb.capacity == 4096); 23 | 24 | char *write_ptr = ring_buffer_write_ptr(&rb); 25 | int amt = sprintf(write_ptr, "hello") + 1; 26 | ring_buffer_advance_write_ptr(&rb, amt); 27 | 28 | assert(ring_buffer_fill_count(&rb) == amt); 29 | assert(ring_buffer_free_count(&rb) == 4096 - amt); 30 | 31 | char *read_ptr = ring_buffer_read_ptr(&rb); 32 | 33 | assert(strcmp(read_ptr, "hello") == 0); 34 | 35 | ring_buffer_advance_read_ptr(&rb, amt); 36 | 37 | assert(ring_buffer_fill_count(&rb) == 0); 38 | assert(ring_buffer_free_count(&rb) == rb.capacity); 39 | 40 | ring_buffer_advance_write_ptr(&rb, 4094); 41 | ring_buffer_advance_read_ptr(&rb, 4094); 42 | amt = sprintf(ring_buffer_write_ptr(&rb), "writing past the end") + 1; 43 | ring_buffer_advance_write_ptr(&rb, amt); 44 | 45 | assert(ring_buffer_fill_count(&rb) == amt); 46 | 47 | assert(strcmp(ring_buffer_read_ptr(&rb), "writing past the end") == 0); 48 | 49 | ring_buffer_advance_read_ptr(&rb, amt); 50 | 51 | assert(ring_buffer_fill_count(&rb) == 0); 52 | assert(ring_buffer_free_count(&rb) == rb.capacity); 53 | } 54 | 55 | static RingBuffer *rb = nullptr; 56 | static const int size = 3528; 57 | static long expected_write_head; 58 | static long expected_read_head; 59 | static atomic_bool done; 60 | static atomic_int write_it; 61 | static atomic_int read_it; 62 | 63 | static void reader_thread_run(void *) { 64 | while (!done) { 65 | read_it += 1; 66 | int fill_count = ring_buffer_fill_count(rb); 67 | assert(fill_count >= 0); 68 | assert(fill_count <= size); 69 | int amount_to_read = min(os_random_double() * 2.0 * fill_count, fill_count); 70 | ring_buffer_advance_read_ptr(rb, amount_to_read); 71 | expected_read_head += amount_to_read; 72 | } 73 | } 74 | 75 | static void writer_thread_run(void *) { 76 | while (!done) { 77 | write_it += 1; 78 | int fill_count = ring_buffer_fill_count(rb); 79 | assert(fill_count >= 0); 80 | assert(fill_count <= size); 81 | int free_count = size - fill_count; 82 | assert(free_count >= 0); 83 | assert(free_count <= size); 84 | int value = min(os_random_double() * 2.0 * free_count, free_count); 85 | ring_buffer_advance_write_ptr(rb, value); 86 | expected_write_head += value; 87 | } 88 | } 89 | 90 | static void threaded_test(void) { 91 | rb = ok_mem(allocate_zero(1)); 92 | assert_no_err(ring_buffer_init(rb, size)); 93 | expected_write_head = 0; 94 | expected_read_head = 0; 95 | read_it = 0; 96 | write_it = 0; 97 | done = false; 98 | 99 | OsThread *reader_thread; 100 | assert_no_err(os_thread_create(reader_thread_run, nullptr, false, &reader_thread)); 101 | 102 | OsThread *writer_thread; 103 | assert_no_err(os_thread_create(writer_thread_run, nullptr, false, &writer_thread)); 104 | 105 | while (read_it < 100000 || write_it < 100000) {} 106 | done = true; 107 | 108 | os_thread_destroy(reader_thread); 109 | os_thread_destroy(writer_thread); 110 | 111 | int fill_count = ring_buffer_fill_count(rb); 112 | int expected_fill_count = expected_write_head - expected_read_head; 113 | assert(fill_count == expected_fill_count); 114 | } 115 | 116 | void test_ring_buffer(void) { 117 | basic_test(); 118 | threaded_test(); 119 | } 120 | -------------------------------------------------------------------------------- /src/gui.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GUI_HPP 2 | #define GUI_HPP 3 | 4 | #include "shader_program_manager.hpp" 5 | #include "freetype.hpp" 6 | #include "list.hpp" 7 | #include "glm.hpp" 8 | #include "hash_map.hpp" 9 | #include "font_size.hpp" 10 | #include "resource_bundle.hpp" 11 | #include "spritesheet.hpp" 12 | #include "gui_window.hpp" 13 | #include "static_geometry.hpp" 14 | #include "glfw.hpp" 15 | #include "event_dispatcher.hpp" 16 | #include "drag_event.hpp" 17 | 18 | struct RenderJob; 19 | 20 | uint32_t hash_int(const int &x); 21 | 22 | class GlobalGlfwContext { 23 | public: 24 | GlobalGlfwContext(); 25 | ~GlobalGlfwContext(); 26 | private: 27 | GlobalGlfwContext(const GlobalGlfwContext ©) = delete; 28 | GlobalGlfwContext &operator=(const GlobalGlfwContext ©) = delete; 29 | }; 30 | 31 | class Gui { 32 | public: 33 | Gui(GenesisContext *context, ResourceBundle *resource_bundle); 34 | ~Gui(); 35 | 36 | void exec(); 37 | 38 | GuiWindow *create_window(int left, int top, int width, int height); 39 | void destroy_window(GuiWindow *window); 40 | 41 | FontSize *get_font_size(int font_size); 42 | 43 | void draw_image(GuiWindow *window, const SpritesheetImage *img, const glm::mat4 &mvp); 44 | void draw_image_color(GuiWindow *window, const SpritesheetImage *img, 45 | const glm::mat4 &mvp, const glm::vec4 &color); 46 | 47 | void start_drag(GuiWindow *gui_window, const MouseEvent *event, DragData *drag_data); 48 | void end_drag(); 49 | 50 | void remove_render_job(RenderJob *rj); 51 | void destroy_render_job(RenderJob *rj); 52 | 53 | OsMutex *gui_mutex; 54 | 55 | 56 | bool _running; 57 | List _window_list; 58 | GuiWindow *_focus_window; 59 | 60 | GlobalGlfwContext _global_glfw_context; 61 | // utility window has 2 purposes. 1. to give us an OpenGL context before a 62 | // real window is created, and 2. block on draw() gives our main loop 63 | // something to block on 64 | GuiWindow *_utility_window; 65 | ShaderProgramManager _shader_program_manager; 66 | StaticGeometry _static_geometry; 67 | 68 | GLFWcursor* cursor_ibeam; 69 | GLFWcursor* cursor_default; 70 | GLFWcursor* cursor_hresize; 71 | GLFWcursor* cursor_vresize; 72 | 73 | FT_Library _ft_library; 74 | FT_Face _default_font_face; 75 | 76 | // key is font size 77 | HashMap _font_size_cache; 78 | 79 | ResourceBundle *_resource_bundle; 80 | ByteBuffer _default_font_buffer; 81 | 82 | List render_jobs; 83 | 84 | Spritesheet _spritesheet; 85 | 86 | const SpritesheetImage *img_entry_dir; 87 | const SpritesheetImage *img_entry_dir_open; 88 | const SpritesheetImage *img_entry_file; 89 | const SpritesheetImage *img_plus; 90 | const SpritesheetImage *img_minus; 91 | const SpritesheetImage *img_microphone; 92 | const SpritesheetImage *img_volume_up; 93 | const SpritesheetImage *img_check; 94 | const SpritesheetImage *img_caret_right; 95 | const SpritesheetImage *img_caret_down; 96 | const SpritesheetImage *img_arrow_up; 97 | const SpritesheetImage *img_arrow_down; 98 | const SpritesheetImage *img_arrow_left; 99 | const SpritesheetImage *img_arrow_right; 100 | const SpritesheetImage *img_music; 101 | const SpritesheetImage *img_plug; 102 | const SpritesheetImage *img_exclamation_circle; 103 | const SpritesheetImage *img_null; 104 | 105 | const SpritesheetImage *img_play_head; 106 | 107 | GenesisContext *_genesis_context; 108 | 109 | EventDispatcher events; 110 | 111 | double fps; 112 | 113 | bool dragging; 114 | DragData *drag_data; 115 | GuiWindow *drag_window; 116 | MouseEvent drag_orig_event; 117 | 118 | GuiWindow *create_utility_window(); 119 | GuiWindow *create_generic_window(bool is_utility, int left, int top, int width, int height); 120 | 121 | Gui(const Gui ©) = delete; 122 | Gui &operator=(const Gui ©) = delete; 123 | 124 | }; 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/gui_window.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GUI_WINDOW_HPP 2 | #define GUI_WINDOW_HPP 3 | 4 | #include "list.hpp" 5 | #include "glm.hpp" 6 | #include "key_event.hpp" 7 | #include "mouse_event.hpp" 8 | #include "drag_event.hpp" 9 | #include "string.hpp" 10 | #include "glfw.hpp" 11 | #include "event_dispatcher.hpp" 12 | #include "atomics.hpp" 13 | #include "os.hpp" 14 | 15 | class Gui; 16 | class Widget; 17 | struct SpritesheetImage; 18 | class MenuWidgetItem; 19 | class MenuWidget; 20 | class ContextMenuWidget; 21 | 22 | class GuiWindow { 23 | public: 24 | GuiWindow(Gui *gui, bool is_normal_window, int left, int top, int width, int height); 25 | ~GuiWindow(); 26 | 27 | void draw(); 28 | 29 | void remove_widget(Widget *widget); 30 | void set_focus_widget(Widget *widget); 31 | 32 | void maximize(); 33 | 34 | void set_cursor_beam(); 35 | void set_cursor_default(); 36 | void set_cursor_hresize(); 37 | void set_cursor_vresize(); 38 | 39 | bool try_mouse_move_event_on_widget(Widget *widget, const MouseEvent *event); 40 | 41 | void fill_rect(const glm::vec4 &color, const glm::mat4 &mvp); 42 | void fill_rect(const glm::vec4 &color, int x, int y, int w, int h); 43 | void draw_image(const SpritesheetImage *img, int x, int y, int w, int h); 44 | void fill_rect_gradient(const glm::vec4 &top_color, const glm::vec4 &bottom_color, const glm::mat4 &mvp); 45 | 46 | void set_clipboard_string(const String &str); 47 | String get_clipboard_string() const; 48 | bool clipboard_has_string() const; 49 | 50 | void set_main_widget(Widget *widget); 51 | // coords are the rectangle that originated the menu. you might only need left and top. 52 | ContextMenuWidget * pop_context_menu(MenuWidgetItem *menu_widget_item, int left, int top, int width, int height); 53 | void refresh_context_menu(); 54 | 55 | void start_drag(const MouseEvent *event, DragData *drag_data); 56 | 57 | void *_userdata; 58 | // index into Gui's list of windows 59 | int _gui_index; 60 | 61 | Gui *gui; 62 | GLFWwindow *window; 63 | GLuint vertex_array_object; 64 | 65 | // pixels 66 | int _width; 67 | int _height; 68 | 69 | // screen coordinates 70 | int _client_width; 71 | int _client_height; 72 | int client_left; 73 | int client_top; 74 | 75 | glm::mat4 _projection; 76 | Widget *_mouse_over_widget; 77 | Widget *_focus_widget; 78 | MenuWidget *menu_widget; 79 | 80 | EventDispatcher events; 81 | 82 | bool _is_iconified; 83 | bool is_visible; 84 | 85 | double _last_click_time; 86 | MouseButton _last_click_button; 87 | double _double_click_timeout; 88 | int dbl_click_count; 89 | 90 | OsThread *thread; 91 | atomic_bool running; 92 | atomic_bool viewport_update_queued; 93 | 94 | Widget *main_widget; 95 | ContextMenuWidget *context_menu; 96 | 97 | void layout_main_widget(); 98 | int get_modifiers(); 99 | void on_mouse_move(const MouseEvent *event); 100 | 101 | bool is_maximized; 102 | Widget *drag_widget; 103 | 104 | void window_iconify_callback(int iconified); 105 | void framebuffer_size_callback(int width, int height); 106 | void window_size_callback(int width, int height); 107 | void key_callback(int key, int scancode, int action, int mods); 108 | void charmods_callback(unsigned int codepoint, int mods); 109 | void window_close_callback(); 110 | void cursor_pos_callback(double xpos, double ypos); 111 | void mouse_button_callback(int button, int action, int mods); 112 | void scroll_callback(double xoffset, double yoffset); 113 | void window_pos_callback(int left, int top); 114 | 115 | void setup_context(); 116 | void teardown_context(); 117 | void destroy_context_menu(); 118 | void got_window_size(int width, int height); 119 | void got_window_pos(int left, int top); 120 | bool widget_is_menu(Widget *widget); 121 | 122 | bool forward_drag_event(Widget *widget, const DragEvent *event); 123 | bool forward_mouse_wheel_event(Widget *widget, const MouseWheelEvent *event); 124 | 125 | }; 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /src/byte_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "byte_buffer.hpp" 2 | 3 | #include "util.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | ByteBuffer::ByteBuffer() { 10 | if (_buffer.append(0)) 11 | panic("out of memory"); 12 | } 13 | 14 | ByteBuffer::ByteBuffer(const char * str) { 15 | if (_buffer.append(0)) 16 | panic("out of memory"); 17 | append(str, strlen(str)); 18 | } 19 | ByteBuffer::ByteBuffer(const ByteBuffer & copy) { 20 | if (_buffer.append(0)) 21 | panic("out of memory"); 22 | append(copy); 23 | } 24 | ByteBuffer::ByteBuffer(const char * str, int length) { 25 | ok_or_panic(_buffer.append(0)); 26 | append(str, (length == -1) ? strlen(str) : length); 27 | } 28 | 29 | void ByteBuffer::append(const ByteBuffer &other) { 30 | append(other.raw(), other.length()); 31 | } 32 | 33 | void ByteBuffer::append(const char *str) { 34 | append(str, strlen(str)); 35 | } 36 | 37 | void ByteBuffer::append(const char *str, int length) { 38 | int prev_length_plus_null = _buffer.length(); 39 | int new_length_plus_null = prev_length_plus_null + length; 40 | if (_buffer.resize(new_length_plus_null)) 41 | panic("out of memory"); 42 | memcpy(_buffer.raw() + prev_length_plus_null - 1, str, length); 43 | _buffer.at(new_length_plus_null - 1) = 0; 44 | } 45 | 46 | void ByteBuffer::format(const char *format, ...) { 47 | va_list ap, ap2; 48 | va_start(ap, format); 49 | va_copy(ap2, ap); 50 | 51 | int ret = vsnprintf(NULL, 0, format, ap); 52 | if (ret < 0) 53 | panic("vsnprintf error"); 54 | 55 | int required_length = ret + 1; 56 | ok_or_panic(_buffer.resize(required_length)); 57 | 58 | ret = vsnprintf(_buffer.raw(), required_length, format, ap2); 59 | if (ret < 0) 60 | panic("vsnprintf error 2"); 61 | 62 | va_end(ap2); 63 | va_end(ap); 64 | } 65 | 66 | int ByteBuffer::index_of_rev(char c) const { 67 | return index_of_rev(c, length() - 1); 68 | } 69 | 70 | int ByteBuffer::index_of_rev(char c, int start) const { 71 | for (off_t i = start; i >= 0; i -= 1) { 72 | if (_buffer.at(i) == c) 73 | return i; 74 | } 75 | return -1; 76 | } 77 | 78 | ByteBuffer ByteBuffer::substring(int start, int end) const { 79 | if (start < 0 || start >= length()) 80 | panic("substring start out of bounds"); 81 | if (end < 0 || end > length()) 82 | panic("substring end out of bounds"); 83 | return ByteBuffer(_buffer.raw() + start, end - start); 84 | } 85 | 86 | ByteBuffer& ByteBuffer::operator= (const ByteBuffer& other) { 87 | if (this != &other) { 88 | if (_buffer.resize(other._buffer.length())) 89 | panic("out of memory"); 90 | memcpy(_buffer.raw(), other.raw(), _buffer.length()); 91 | } 92 | return *this; 93 | } 94 | 95 | void ByteBuffer::split(const char *split_by, List &out) const { 96 | const char *split_ptr = split_by; 97 | 98 | bool in_match = false; 99 | if (out.resize(1)) 100 | panic("out of memory"); 101 | ByteBuffer *current = &out.at(0); 102 | 103 | for (const char *buf_ptr = raw(); *buf_ptr; buf_ptr += 1) { 104 | if (*buf_ptr == *split_ptr) { 105 | in_match = true; 106 | split_ptr += 1; 107 | if (!*split_ptr) { 108 | split_ptr = split_by; 109 | in_match = false; 110 | if (out.resize(out.length() + 1)) 111 | panic("out of memory"); 112 | current = &out.at(out.length() - 1); 113 | } 114 | continue; 115 | } else if (in_match) { 116 | split_ptr = split_by; 117 | in_match = false; 118 | } 119 | current->append(buf_ptr, 1); 120 | } 121 | } 122 | 123 | ByteBuffer ByteBuffer::to_string() const { 124 | ByteBuffer result; 125 | result.resize(_buffer.length() * 2); 126 | for (int i = 0; i < _buffer.length() - 1; i += 1) { 127 | sprintf(result.raw() + i * 2, "%02x", *((uint8_t*)(_buffer.raw() + i))); 128 | } 129 | return result; 130 | } 131 | -------------------------------------------------------------------------------- /example/delay.c: -------------------------------------------------------------------------------- 1 | #include "genesis.h" 2 | #include 3 | #include 4 | #include 5 | 6 | // adds a delay effect to the default recording device and plays it over 7 | // default playback device 8 | 9 | static void connect_audio_nodes(struct GenesisNode *source, struct GenesisNode *dest) { 10 | int err = genesis_connect_audio_nodes(source, dest); 11 | if (err) { 12 | fprintf(stderr, "unable to connect audio ports: %s\n", genesis_strerror(err)); 13 | fprintf(stderr, "%s -> %s\n", 14 | genesis_node_descriptor_name(genesis_node_descriptor(source)), 15 | genesis_node_descriptor_name(genesis_node_descriptor(dest))); 16 | exit(1); 17 | } 18 | } 19 | 20 | static int usage(char *exe) { 21 | fprintf(stderr, "Usage: %s [--no-delay]\n", exe); 22 | return 1; 23 | } 24 | 25 | static void on_underrun(void *userdata) { 26 | static int underrun_count = 0; 27 | fprintf(stderr, "buffer underrun %d\n", ++underrun_count); 28 | } 29 | 30 | int main(int argc, char **argv) { 31 | bool no_delay = false; 32 | 33 | for (int i = 1; i < argc; i += 1) { 34 | char *arg = argv[i]; 35 | if (arg[0] == '-' && arg[1] == '-') { 36 | arg += 2; 37 | if (strcmp(arg, "no-delay") == 0) { 38 | no_delay = true; 39 | } else { 40 | return usage(argv[0]); 41 | } 42 | } else { 43 | return usage(argv[0]); 44 | } 45 | } 46 | 47 | struct GenesisContext *context; 48 | int err = genesis_context_create(&context); 49 | if (err) { 50 | fprintf(stderr, "unable to create context: %s\n", genesis_strerror(err)); 51 | return 1; 52 | } 53 | 54 | struct GenesisPipeline *pipeline; 55 | if ((err = genesis_pipeline_create(context, &pipeline))) { 56 | fprintf(stderr, "unable to create pipeline: %s\n", genesis_strerror(err)); 57 | return 1; 58 | } 59 | 60 | genesis_pipeline_set_underrun_callback(pipeline, on_underrun, NULL); 61 | 62 | // block until we have audio devices list 63 | genesis_flush_events(context); 64 | 65 | struct SoundIoDevice *out_device = genesis_get_default_output_device(context); 66 | struct SoundIoDevice *in_device = genesis_get_default_input_device(context); 67 | 68 | if (!out_device || !in_device) { 69 | fprintf(stderr, "error getting playback or recording device\n"); 70 | return 1; 71 | } 72 | 73 | struct GenesisNodeDescriptor *playback_node_descr; 74 | err = genesis_audio_device_create_node_descriptor(pipeline, out_device, &playback_node_descr); 75 | if (err) { 76 | fprintf(stderr, "unable to get node info for output device: %s\n", genesis_strerror(err)); 77 | return 1; 78 | } 79 | 80 | struct GenesisNode *playback_node = genesis_node_descriptor_create_node(playback_node_descr); 81 | if (!playback_node) { 82 | fprintf(stderr, "unable to create out node\n"); 83 | return 1; 84 | } 85 | 86 | struct GenesisNodeDescriptor *recording_node_descr; 87 | err = genesis_audio_device_create_node_descriptor(pipeline, in_device, &recording_node_descr); 88 | if (err) { 89 | fprintf(stderr, "unable to get node info for output device: %s\n", genesis_strerror(err)); 90 | return 1; 91 | } 92 | 93 | soundio_device_unref(in_device); 94 | soundio_device_unref(out_device); 95 | 96 | struct GenesisNode *recording_node = genesis_node_descriptor_create_node(recording_node_descr); 97 | if (!recording_node) { 98 | fprintf(stderr, "unable to create out node\n"); 99 | return 1; 100 | } 101 | 102 | struct GenesisNodeDescriptor *delay_descr = genesis_node_descriptor_find(pipeline, "delay"); 103 | if (!delay_descr) { 104 | fprintf(stderr, "unable to find delay filter\n"); 105 | return 1; 106 | } 107 | fprintf(stderr, "delay: %s\n", genesis_node_descriptor_description(delay_descr)); 108 | 109 | struct GenesisNode *delay_node = genesis_node_descriptor_create_node(delay_descr); 110 | if (!delay_node) { 111 | fprintf(stderr, "unable to create delay node\n"); 112 | return 1; 113 | } 114 | 115 | if (no_delay) { 116 | connect_audio_nodes(recording_node, playback_node); 117 | } else { 118 | connect_audio_nodes(recording_node, delay_node); 119 | connect_audio_nodes(delay_node, playback_node); 120 | } 121 | 122 | genesis_pipeline_start(pipeline, 0.0); 123 | 124 | for (;;) 125 | genesis_wait_events(context); 126 | } 127 | -------------------------------------------------------------------------------- /src/os.hpp: -------------------------------------------------------------------------------- 1 | #ifndef OS_HPP 2 | #define OS_HPP 3 | 4 | #include "byte_buffer.hpp" 5 | #include "string.hpp" 6 | #include "sha_256_hasher.hpp" 7 | 8 | #include 9 | 10 | struct OsDirEntry { 11 | ByteBuffer name; 12 | bool is_dir; 13 | bool is_file; 14 | bool is_link; 15 | bool is_hidden; 16 | int64_t size; 17 | long mtime; 18 | int ref_count; 19 | }; 20 | 21 | int os_init(int (*init_once)(void)); 22 | 23 | void os_get_home_dir(ByteBuffer &out); 24 | void os_get_app_dir(ByteBuffer &out); 25 | void os_get_projects_dir(ByteBuffer &out); 26 | void os_get_app_config_dir(ByteBuffer &out); 27 | void os_get_app_config_path(ByteBuffer &out); 28 | void os_get_samples_dir(ByteBuffer &out); 29 | 30 | uint32_t os_random_uint32(void); // 32 bits of entropy 31 | uint64_t os_random_uint64(void); // 64 bits of entropy 32 | double os_random_double(void); // 32 bits of entropy in range [0.0, 1.0) 33 | void os_open_in_browser(const String &url); 34 | double os_get_time(void); 35 | String os_get_user_name(void); 36 | 37 | int os_delete(const char *path); 38 | int os_rename_clobber(const char *source, const char *dest); 39 | 40 | struct OsTempFile { 41 | ByteBuffer path; 42 | FILE *file; 43 | }; 44 | int os_create_temp_file(const char *dir, OsTempFile *out_tmp_file); 45 | 46 | int os_file_flush(FILE *file); 47 | int os_file_size(FILE *file, long *out_size); 48 | 49 | int os_mkdirp(ByteBuffer path); 50 | ByteBuffer os_path_dirname(ByteBuffer path); 51 | ByteBuffer os_path_basename(const ByteBuffer &path); 52 | void os_path_join(ByteBuffer &out, ByteBuffer left, ByteBuffer right); 53 | ByteBuffer os_path_extension(ByteBuffer path); 54 | void os_path_remove_extension(ByteBuffer &path); 55 | 56 | // call unref on each entry when done 57 | int os_copy_no_clobber(const char *source_path, const char *dest_dir, 58 | const char *prefix, const char *dest_extension, 59 | ByteBuffer &out_path, Sha256Hasher *hasher); 60 | int os_copy(const char *source_path, const char *dest_path, Sha256Hasher *hasher); 61 | int os_readdir(const char *dir, List &out_entries); 62 | void os_dir_entry_ref(OsDirEntry *dir_entry); 63 | void os_dir_entry_unref(OsDirEntry *dir_entry); 64 | 65 | 66 | 67 | double os_get_time(void); 68 | 69 | struct OsThread; 70 | int os_thread_create( 71 | void (*run)(void *arg), void *arg, 72 | bool high_priority, 73 | struct OsThread ** out_thread); 74 | 75 | void os_thread_destroy(struct OsThread *thread); 76 | 77 | 78 | struct OsMutex; 79 | struct OsMutex *os_mutex_create(void); 80 | void os_mutex_destroy(struct OsMutex *mutex); 81 | void os_mutex_lock(struct OsMutex *mutex); 82 | void os_mutex_unlock(struct OsMutex *mutex); 83 | 84 | struct OsCond; 85 | struct OsCond *os_cond_create(void); 86 | void os_cond_destroy(struct OsCond *cond); 87 | 88 | // locked_mutex is optional. On systems that use mutexes for conditions, if you 89 | // pass NULL, a mutex will be created and locked/unlocked for you. On systems 90 | // that do not use mutexes for conditions, no mutex handling is necessary. If 91 | // you already have a locked mutex available, pass it; this will be better on 92 | // systems that use mutexes for conditions. Wakes at least one thread waiting 93 | // on cond. 94 | void os_cond_signal(struct OsCond *cond, struct OsMutex *locked_mutex); 95 | // Wakes all threads waiting on cond. 96 | void os_cond_broadcast(struct OsCond *cond, struct OsMutex *locked_mutex); 97 | void os_cond_timed_wait(struct OsCond *cond, struct OsMutex *locked_mutex, double seconds); 98 | void os_cond_wait(struct OsCond *cond, struct OsMutex *locked_mutex); 99 | 100 | 101 | int os_page_size(void); 102 | 103 | struct OsMirroredMemory { 104 | size_t capacity; 105 | char *address; 106 | void *priv; 107 | }; 108 | 109 | // returned capacity might be increased from capacity to be a multiple of the 110 | // system page size 111 | int os_init_mirrored_memory(struct OsMirroredMemory *mem, size_t capacity); 112 | void os_deinit_mirrored_memory(struct OsMirroredMemory *mem); 113 | 114 | int os_concurrency(void); 115 | 116 | struct OsMutexLocker { 117 | OsMutexLocker(OsMutex *mutex) { 118 | this->mutex = mutex; 119 | os_mutex_lock(mutex); 120 | } 121 | 122 | ~OsMutexLocker() { 123 | os_mutex_unlock(mutex); 124 | } 125 | 126 | OsMutex *mutex; 127 | }; 128 | 129 | int os_get_current_year(void); 130 | 131 | void os_spawn_process(const char *exe, const List &args, bool detached); 132 | 133 | int os_futex_wait(int *address, int val); 134 | int os_futex_wake(int *address, int count); 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /src/select_widget.cpp: -------------------------------------------------------------------------------- 1 | #include "select_widget.hpp" 2 | #include "gui_window.hpp" 3 | #include "gui.hpp" 4 | #include "color.hpp" 5 | #include "menu_widget.hpp" 6 | 7 | static void on_select_choice(void *userdata) { 8 | SelectWidgetItem *item = (SelectWidgetItem *)userdata; 9 | SelectWidget *select_widget = item->parent; 10 | select_widget->selected_index = item->index; 11 | select_widget->events.trigger(EventSelectedIndexChanged); 12 | } 13 | 14 | static void on_context_menu_destroy(ContextMenuWidget *context_menu_widget) { 15 | SelectWidget *select_widget = (SelectWidget *)context_menu_widget->userdata; 16 | 17 | select_widget->context_menu_open = false; 18 | } 19 | 20 | SelectWidget::~SelectWidget() { 21 | destroy(context_menu, 1); 22 | } 23 | 24 | SelectWidget::SelectWidget(GuiWindow *gui_window) : 25 | Widget(gui_window), 26 | label(gui_window->gui) 27 | { 28 | arrow_icon_img = gui->img_caret_down; 29 | padding_left = 4; 30 | padding_right = 4; 31 | padding_top = 4; 32 | padding_bottom = 4; 33 | icon_spacing = 4; 34 | hovering = false; 35 | text_color = color_fg_text(); 36 | selected_index = -1; 37 | 38 | context_menu = create(gui_window); 39 | context_menu_open = false; 40 | 41 | update_model(); 42 | } 43 | 44 | void SelectWidget::draw(const glm::mat4 &projection) { 45 | bg.draw(gui_window, projection); 46 | if (selected_index >= 0) 47 | label.draw(projection * label_model, text_color); 48 | 49 | gui->draw_image_color(gui_window, arrow_icon_img, projection * arrow_icon_model, text_color); 50 | } 51 | 52 | void SelectWidget::update_model() { 53 | bg.set_scheme(SunkenBoxSchemeRaisedBorders); 54 | bg.update(this, 0, 0, width, height); 55 | 56 | label_model = transform2d(padding_left, padding_top); 57 | 58 | float icon_h = label.height(); 59 | float icon_w = icon_h; 60 | float scale_x = ((float)icon_w) / ((float)arrow_icon_img->width); 61 | float scale_y = ((float)icon_h) / ((float)arrow_icon_img->height); 62 | arrow_icon_model = transform2d(width - padding_right - icon_w - icon_spacing, padding_top, scale_x, scale_y); 63 | } 64 | 65 | void SelectWidget::clear() { 66 | items.clear(); 67 | context_menu->clear(); 68 | clear_context_menu(); 69 | } 70 | 71 | void SelectWidget::append_choice(const String &choice) { 72 | ok_or_panic(items.add_one()); 73 | SelectWidgetItem *item = &items.last(); 74 | item->parent = this; 75 | item->name = choice; 76 | item->menu_item = context_menu->add_menu(choice, -1, no_shortcut()); 77 | set_activate_handlers(); 78 | clear_context_menu(); 79 | } 80 | 81 | 82 | void SelectWidget::set_activate_handlers() { 83 | for (int i = 0; i < items.length(); i += 1) { 84 | SelectWidgetItem *item = &items.at(i); 85 | item->index = i; 86 | item->menu_item->set_activate_handler(on_select_choice, item); 87 | } 88 | } 89 | 90 | void SelectWidget::clear_context_menu() { 91 | if (context_menu_open) { 92 | gui_window->destroy_context_menu(); 93 | } 94 | } 95 | 96 | void SelectWidget::select_index(int index) { 97 | selected_index = index; 98 | if (selected_index >= 0) { 99 | SelectWidgetItem *item = &items.at(selected_index); 100 | label.set_text(item->name); 101 | label.update(); 102 | update_model(); 103 | } 104 | } 105 | 106 | void SelectWidget::on_mouse_move(const MouseEvent *event) { 107 | switch (event->action) { 108 | case MouseActionMove: 109 | break; 110 | case MouseActionDown: 111 | { 112 | context_menu_open = true; 113 | ContextMenuWidget *context_menu_widget = pop_context_menu(context_menu, 0, 0, width, height); 114 | context_menu_widget->userdata = this; 115 | context_menu_widget->on_destroy = on_context_menu_destroy; 116 | break; 117 | } 118 | case MouseActionUp: 119 | break; 120 | } 121 | } 122 | 123 | void SelectWidget::on_mouse_out(const MouseEvent *event) { 124 | hovering = false; 125 | } 126 | 127 | void SelectWidget::on_mouse_over(const MouseEvent *event) { 128 | hovering = true; 129 | } 130 | 131 | int SelectWidget::min_height() const { 132 | return padding_top + label.height() + padding_bottom; 133 | } 134 | 135 | int SelectWidget::max_height() const { 136 | return min_height(); 137 | } 138 | 139 | int SelectWidget::min_width() const { 140 | return padding_left + label.width() + icon_spacing + label.height() + padding_right; 141 | } 142 | -------------------------------------------------------------------------------- /test/ordered_map_file_test.cpp: -------------------------------------------------------------------------------- 1 | #include "ordered_map_file_test.hpp" 2 | #include "ordered_map_file.hpp" 3 | #include "os.hpp" 4 | 5 | static const char *tmp_file_path = "/tmp/genesis_test.gdaw"; 6 | 7 | static void delete_tmp_file(void) { 8 | os_delete(tmp_file_path); 9 | } 10 | 11 | static void test_open_close(void) { 12 | OrderedMapFile *omf; 13 | int err = ordered_map_file_open(tmp_file_path, &omf); 14 | assert(err == 0); 15 | assert(omf); 16 | ordered_map_file_done_reading(omf); 17 | ordered_map_file_close(omf); 18 | delete_tmp_file(); 19 | } 20 | 21 | static void test_bogus_file(void) { 22 | FILE *f = fopen(tmp_file_path, "wb"); 23 | fprintf(f, "aoeuaouaeouaouaoeu"); 24 | fclose(f); 25 | 26 | OrderedMapFile *omf = nullptr; 27 | int err = ordered_map_file_open(tmp_file_path, &omf); 28 | assert(err == GenesisErrorInvalidFormat); 29 | assert(!omf); 30 | 31 | delete_tmp_file(); 32 | } 33 | 34 | static void test_simple_data(void) { 35 | { 36 | OrderedMapFile *omf; 37 | int err = ordered_map_file_open(tmp_file_path, &omf); 38 | assert(err == 0); 39 | assert(omf); 40 | ordered_map_file_done_reading(omf); 41 | 42 | { 43 | OrderedMapFileBatch *batch = ordered_map_file_batch_create(omf); 44 | 45 | OrderedMapFileBuffer *key = ordered_map_file_buffer_create(1); 46 | OrderedMapFileBuffer *value = ordered_map_file_buffer_create(8); 47 | 48 | key->data[0] = "H"[0]; 49 | memcpy(value->data, "aoeuasdf", 8); 50 | 51 | ordered_map_file_batch_put(batch, key, value); 52 | 53 | err = ordered_map_file_batch_exec(batch); 54 | assert(err == 0); 55 | } 56 | 57 | ordered_map_file_close(omf); 58 | } 59 | OrderedMapFile *omf = nullptr; 60 | int err = ordered_map_file_open(tmp_file_path, &omf); 61 | assert(err == 0); 62 | assert(omf); 63 | 64 | assert(ordered_map_file_count(omf) == 1); 65 | 66 | int index = ordered_map_file_find_key(omf, "bogus key"); 67 | assert(index == -1); 68 | 69 | index = ordered_map_file_find_key(omf, "H"); 70 | assert(index == 0); 71 | ByteBuffer *key; 72 | ByteBuffer value; 73 | err = ordered_map_file_get(omf, index, &key, value); 74 | assert(err == 0); 75 | assert(ByteBuffer::compare(*key, "H") == 0); 76 | assert(ByteBuffer::compare(value, "aoeuasdf") == 0); 77 | 78 | ordered_map_file_done_reading(omf); 79 | ordered_map_file_close(omf); 80 | delete_tmp_file(); 81 | } 82 | 83 | static void test_many_data(void) { 84 | OrderedMapFile *omf; 85 | int err = ordered_map_file_open(tmp_file_path, &omf); 86 | assert(err == 0); 87 | assert(omf); 88 | ordered_map_file_done_reading(omf); 89 | 90 | for (int i = 0; i < 400; i += 1) { 91 | OrderedMapFileBatch *batch = ordered_map_file_batch_create(omf); 92 | 93 | OrderedMapFileBuffer *key = ordered_map_file_buffer_create(4); 94 | OrderedMapFileBuffer *value = ordered_map_file_buffer_create(4); 95 | 96 | sprintf(key->data, "%03d", i); 97 | sprintf(value->data, "%03d", i); 98 | 99 | ordered_map_file_batch_put(batch, key, value); 100 | 101 | int err = ordered_map_file_batch_exec(batch); 102 | assert(err == 0); 103 | } 104 | 105 | ordered_map_file_close(omf); 106 | 107 | omf = nullptr; 108 | err = ordered_map_file_open(tmp_file_path, &omf); 109 | assert(err == 0); 110 | assert(omf); 111 | 112 | assert(ordered_map_file_count(omf) == 400); 113 | 114 | ByteBuffer expected_key; 115 | expected_key.format("%03d", 400); 116 | assert(ordered_map_file_find_key(omf, expected_key) == -1); 117 | 118 | assert(ordered_map_file_find_prefix(omf, "10") == 100); 119 | assert(ordered_map_file_find_prefix(omf, "20") == 200); 120 | assert(ordered_map_file_find_prefix(omf, "30") == 300); 121 | 122 | for (int i = 0; i < 400; i += 1) { 123 | expected_key.format("%03d", i); 124 | expected_key.resize(4); 125 | int index = ordered_map_file_find_key(omf, expected_key); 126 | assert(index == i); 127 | 128 | ByteBuffer *key; 129 | ByteBuffer value; 130 | err = ordered_map_file_get(omf, i, &key, value); 131 | assert(err == 0); 132 | assert(ByteBuffer::compare(*key, expected_key) == 0); 133 | assert(ByteBuffer::compare(value, expected_key) == 0); 134 | } 135 | 136 | ordered_map_file_done_reading(omf); 137 | ordered_map_file_close(omf); 138 | delete_tmp_file(); 139 | } 140 | 141 | void test_ordered_map_file(void) { 142 | delete_tmp_file(); 143 | test_open_close(); 144 | test_bogus_file(); 145 | test_simple_data(); 146 | test_many_data(); 147 | } 148 | -------------------------------------------------------------------------------- /src/shader_program_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "shader_program_manager.hpp" 2 | #include "debug_gl.hpp" 3 | 4 | ShaderProgramManager::ShaderProgramManager() : 5 | _texture_shader_program(R"VERTEX( 6 | 7 | #version 150 core 8 | 9 | in vec3 VertexPosition; 10 | in vec2 TexCoord; 11 | 12 | out vec2 FragTexCoord; 13 | 14 | uniform mat4 MVP; 15 | 16 | void main(void) 17 | { 18 | FragTexCoord = TexCoord; 19 | gl_Position = MVP * vec4(VertexPosition, 1.0); 20 | } 21 | 22 | )VERTEX", R"FRAGMENT( 23 | 24 | #version 150 core 25 | 26 | in vec2 FragTexCoord; 27 | out vec4 FragColor; 28 | 29 | uniform sampler2D Tex; 30 | 31 | void main(void) 32 | { 33 | FragColor = texture(Tex, FragTexCoord); 34 | } 35 | 36 | )FRAGMENT", NULL), 37 | _text_shader_program(R"VERTEX( 38 | 39 | #version 150 core 40 | 41 | in vec3 VertexPosition; 42 | in vec2 TexCoord; 43 | 44 | out vec2 FragTexCoord; 45 | 46 | uniform mat4 MVP; 47 | 48 | void main(void) 49 | { 50 | FragTexCoord = TexCoord; 51 | gl_Position = MVP * vec4(VertexPosition, 1.0); 52 | } 53 | 54 | )VERTEX", R"FRAGMENT( 55 | 56 | #version 150 core 57 | 58 | in vec2 FragTexCoord; 59 | out vec4 FragColor; 60 | 61 | uniform sampler2D Tex; 62 | uniform vec4 Color; 63 | 64 | void main(void) 65 | { 66 | FragColor = vec4(1, 1, 1, texture(Tex, FragTexCoord).r) * Color; 67 | } 68 | 69 | )FRAGMENT", NULL), 70 | _primitive_shader_program(R"VERTEX( 71 | 72 | #version 150 core 73 | 74 | in vec3 VertexPosition; 75 | 76 | uniform mat4 MVP; 77 | 78 | void main(void) { 79 | gl_Position = MVP * vec4(VertexPosition, 1.0); 80 | } 81 | 82 | )VERTEX", R"FRAGMENT( 83 | 84 | #version 150 core 85 | 86 | out vec4 FragColor; 87 | 88 | uniform vec4 Color; 89 | 90 | void main(void) { 91 | FragColor = Color; 92 | } 93 | 94 | )FRAGMENT", NULL), 95 | texture_color_program(R"VERTEX( 96 | 97 | #version 150 core 98 | 99 | in vec3 VertexPosition; 100 | in vec2 TexCoord; 101 | 102 | out vec2 FragTexCoord; 103 | 104 | uniform mat4 MVP; 105 | 106 | void main(void) 107 | { 108 | FragTexCoord = TexCoord; 109 | gl_Position = MVP * vec4(VertexPosition, 1.0); 110 | } 111 | 112 | )VERTEX", R"FRAGMENT( 113 | 114 | #version 150 core 115 | 116 | in vec2 FragTexCoord; 117 | out vec4 FragColor; 118 | 119 | uniform sampler2D Tex; 120 | uniform vec4 Color; 121 | 122 | void main(void) 123 | { 124 | FragColor = vec4(1, 1, 1, texture(Tex, FragTexCoord).a) * Color; 125 | } 126 | 127 | )FRAGMENT", NULL), 128 | gradient_program(R"VERTEX( 129 | 130 | #version 150 core 131 | 132 | in vec3 VertexPosition; 133 | out float MixAmt; 134 | 135 | uniform mat4 MVP; 136 | 137 | void main(void) { 138 | MixAmt = clamp(0, VertexPosition.y, 1); 139 | gl_Position = MVP * vec4(VertexPosition, 1.0); 140 | } 141 | 142 | )VERTEX", R"FRAGMENT( 143 | 144 | #version 150 core 145 | 146 | in float MixAmt; 147 | out vec4 FragColor; 148 | 149 | uniform vec4 ColorTop; 150 | uniform vec4 ColorBottom; 151 | 152 | void main(void) { 153 | FragColor = ColorBottom * MixAmt + ColorTop * (1 - MixAmt); 154 | } 155 | 156 | )FRAGMENT", NULL) 157 | { 158 | _texture_attrib_tex_coord = _texture_shader_program.attrib_location("TexCoord"); 159 | _texture_attrib_position = _texture_shader_program.attrib_location("VertexPosition"); 160 | _texture_uniform_mvp = _texture_shader_program.uniform_location("MVP"); 161 | _texture_uniform_tex = _texture_shader_program.uniform_location("Tex"); 162 | 163 | _text_attrib_tex_coord = _text_shader_program.attrib_location("TexCoord"); 164 | _text_attrib_position = _text_shader_program.attrib_location("VertexPosition"); 165 | _text_uniform_mvp = _text_shader_program.uniform_location("MVP"); 166 | _text_uniform_tex = _text_shader_program.uniform_location("Tex"); 167 | _text_uniform_color = _text_shader_program.uniform_location("Color"); 168 | 169 | _primitive_attrib_position = _primitive_shader_program.attrib_location("VertexPosition"); 170 | _primitive_uniform_mvp = _primitive_shader_program.uniform_location("MVP"); 171 | _primitive_uniform_color = _primitive_shader_program.uniform_location("Color"); 172 | 173 | texture_color_attrib_tex_coord = texture_color_program.attrib_location("TexCoord"); 174 | texture_color_attrib_position = texture_color_program.attrib_location("VertexPosition"); 175 | texture_color_uniform_mvp = texture_color_program.uniform_location("MVP"); 176 | texture_color_uniform_tex = texture_color_program.uniform_location("Tex"); 177 | texture_color_uniform_color = texture_color_program.uniform_location("Color"); 178 | 179 | gradient_attrib_position = gradient_program.attrib_location("VertexPosition"); 180 | gradient_uniform_mvp = gradient_program.uniform_location("MVP"); 181 | gradient_uniform_color_top = gradient_program.uniform_location("ColorTop"); 182 | gradient_uniform_color_bottom = gradient_program.uniform_location("ColorBottom"); 183 | 184 | assert_no_gl_error(); 185 | } 186 | 187 | ShaderProgramManager::~ShaderProgramManager() { 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/genesis.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENESIS_HPP 2 | #define GENESIS_HPP 3 | 4 | #include "genesis.h" 5 | #include "list.hpp" 6 | #include "midi_hardware.hpp" 7 | #include "os.hpp" 8 | #include "thread_safe_queue.hpp" 9 | #include "ring_buffer.hpp" 10 | #include "atomic_double.hpp" 11 | #include "atomics.hpp" 12 | 13 | struct GenesisPipeline; 14 | 15 | struct GenesisContext { 16 | GenesisSoundBackend *sound_backend_list; 17 | int sound_backend_count; 18 | void (*devices_change_callback)(void *userdata); 19 | void *devices_change_callback_userdata; 20 | void (*sound_backend_disconnect_callback)(void *userdata); 21 | void *sound_backend_disconnect_userdata; 22 | 23 | MidiHardware *midi_hardware; 24 | void (*midi_change_callback)(void *userdata); 25 | void *midi_change_callback_userdata; 26 | 27 | OsCond *events_cond; 28 | OsMutex *events_mutex; 29 | void (*event_callback)(void *userdata); 30 | void *event_callback_userdata; 31 | 32 | List out_formats; 33 | List in_formats; 34 | 35 | List pipelines; 36 | }; 37 | 38 | struct GenesisPipeline { 39 | GenesisContext *context; 40 | 41 | OsThread **thread_pool; 42 | int thread_pool_size; 43 | atomic_int threads_paused; 44 | 45 | void (*underrun_callback)(void *userdata); 46 | void *underrun_callback_userdata; 47 | atomic_flag stream_fail_flag; 48 | 49 | List node_descriptors; 50 | List nodes; 51 | atomic_bool running; 52 | atomic_int paused; 53 | ThreadSafeQueue task_queue; 54 | double latency; 55 | double actual_latency; 56 | 57 | // The sample rate that we use if a range of sample rates are available. For example 58 | // if a device supports 44100 - 96000, and target_sample_rate is 48000, then 48000 59 | // is chosen. Otherwise the closest supported rate is chosen. 60 | int target_sample_rate; 61 | 62 | SoundIoChannelLayout channel_layout; 63 | }; 64 | 65 | struct GenesisPortDescriptor { 66 | enum GenesisPortType port_type; 67 | char *name; 68 | 69 | int (*connect)(struct GenesisPort *port, struct GenesisPort *other_port); 70 | void (*disconnect)(struct GenesisPort *port, struct GenesisPort *other_port); 71 | }; 72 | 73 | struct GenesisEventsPortDescriptor { 74 | struct GenesisPortDescriptor port_descriptor; 75 | }; 76 | 77 | struct GenesisAudioPortDescriptor { 78 | struct GenesisPortDescriptor port_descriptor; 79 | 80 | bool channel_layout_fixed; 81 | // if channel_layout_fixed is true then this is the index 82 | // of the other port that it is the same as, or -1 if it is fixed 83 | // to the value of channel_layout 84 | int same_channel_layout_index; 85 | struct SoundIoChannelLayout channel_layout; 86 | 87 | bool sample_rate_fixed; 88 | // if sample_rate_fixed is true then this is the index 89 | // of the other port that it is the same as, or -1 if it is fixed 90 | // to the value of sample_rate 91 | int same_sample_rate_index; 92 | int sample_rate; 93 | 94 | // Set this to true if we should kick off the audio graph by running 95 | // nodes attached to this port. 96 | bool is_sink; 97 | }; 98 | 99 | struct GenesisNodeDescriptor { 100 | struct GenesisPipeline *pipeline; 101 | char *name; 102 | char *description; 103 | List port_descriptors; 104 | int (*create)(struct GenesisNode *node); 105 | void (*destroy)(struct GenesisNode *node); 106 | void (*run)(struct GenesisNode *node); 107 | void (*seek)(struct GenesisNode *node); 108 | int (*activate)(struct GenesisNode *node); 109 | void (*deactivate)(struct GenesisNode *node); 110 | void (*pause)(struct GenesisNode *node); 111 | int set_index; 112 | double min_software_latency; 113 | 114 | void *userdata; 115 | void (*destroy_descriptor)(struct GenesisNodeDescriptor *); 116 | }; 117 | 118 | struct GenesisPort { 119 | struct GenesisPortDescriptor *descriptor; 120 | struct GenesisNode *node; 121 | struct GenesisPort *input_from; 122 | struct GenesisPort *output_to; 123 | }; 124 | 125 | struct GenesisAudioPort { 126 | struct GenesisPort port; 127 | struct SoundIoChannelLayout channel_layout; 128 | int sample_rate; 129 | RingBuffer sample_buffer; 130 | int sample_buffer_err; 131 | int sample_buffer_size; // in bytes 132 | int bytes_per_frame; 133 | }; 134 | 135 | struct GenesisEventsPort { 136 | struct GenesisPort port; 137 | RingBuffer event_buffer; 138 | int event_buffer_err; 139 | AtomicDouble time_available; // in whole notes 140 | AtomicDouble time_requested; // in whole notes 141 | }; 142 | 143 | struct GenesisNode { 144 | struct GenesisNodeDescriptor *descriptor; 145 | int port_count; 146 | struct GenesisPort **ports; 147 | int set_index; // index into context->nodes 148 | atomic_bool being_processed; 149 | double timestamp; // in whole notes 150 | void *userdata; 151 | bool constructed; 152 | }; 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /src/track_editor_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TRACK_EDITOR_WIDGET_HPP 2 | #define TRACK_EDITOR_WIDGET_HPP 3 | 4 | #include "widget.hpp" 5 | #include "sunken_box.hpp" 6 | 7 | struct Project; 8 | struct AudioGraph; 9 | struct Track; 10 | class Label; 11 | class MenuWidgetItem; 12 | class ScrollBarWidget; 13 | struct DraggedSampleFile; 14 | struct AudioAsset; 15 | struct AudioClip; 16 | struct AudioClipSegment; 17 | struct SpritesheetImage; 18 | 19 | class TrackEditorWidget : public Widget { 20 | public: 21 | TrackEditorWidget(GuiWindow *gui_window, AudioGraph *audio_graph); 22 | ~TrackEditorWidget() override; 23 | 24 | void draw(const glm::mat4 &projection) override; 25 | void on_resize() override { update_model(); } 26 | void on_mouse_move(const MouseEvent *event) override; 27 | void on_mouse_wheel(const MouseWheelEvent *event) override; 28 | void on_drag(const DragEvent *) override; 29 | 30 | 31 | AudioGraph *audio_graph; 32 | Project *project; 33 | 34 | int timeline_top; 35 | int timeline_bottom; 36 | int timeline_height; 37 | int track_head_width; 38 | int track_height; 39 | int head_left; 40 | int body_left; 41 | int track_area_bottom; 42 | 43 | int track_name_label_padding_left; 44 | int track_name_label_padding_top; 45 | 46 | glm::vec4 track_name_color; 47 | glm::vec4 track_main_bg_color; 48 | glm::vec4 timeline_bg_color; 49 | 50 | glm::vec4 dark_border_color; 51 | glm::vec4 light_border_color; 52 | 53 | SunkenBox timeline_bg; 54 | glm::mat4 track_area_stencil; 55 | glm::mat4 widget_stencil; 56 | 57 | const SpritesheetImage *play_head_icon; 58 | glm::vec4 play_head_color; 59 | glm::mat4 play_head_icon_model; 60 | glm::mat4 play_head_model; 61 | glm::vec4 timeline_bottom_border_color; 62 | glm::mat4 timeline_bottom_border_model; 63 | 64 | double pixels_per_whole_note; 65 | 66 | struct DisplayAudioClipSegment; 67 | struct GuiAudioClipSegment { 68 | AudioClipSegment *segment; 69 | DisplayAudioClipSegment *display_segment; 70 | int left; 71 | int right; 72 | int top; 73 | int bottom; 74 | }; 75 | 76 | struct DisplayAudioClipSegment { 77 | GuiAudioClipSegment *gui_segment; 78 | SunkenBox title_bar; 79 | SunkenBox body; 80 | Label *label; 81 | glm::mat4 label_model; 82 | 83 | // adjusted for scroll position 84 | int left; 85 | int right; 86 | int top; 87 | int bottom; 88 | }; 89 | 90 | struct DisplayTrack; 91 | struct GuiTrack { 92 | Track *track; 93 | DisplayTrack *display_track; 94 | List gui_audio_clip_segments; 95 | int left; 96 | int right; 97 | int top; 98 | int bottom; 99 | }; 100 | 101 | struct DisplayTrack { 102 | GuiTrack *gui_track; 103 | SunkenBox head_bg; 104 | SunkenBox body_bg; 105 | glm::mat4 border_top_model; 106 | glm::mat4 border_bottom_model; 107 | Label *track_name_label; 108 | glm::mat4 track_name_label_model; 109 | 110 | List display_audio_clip_segments; 111 | int display_audio_clip_segment_count; 112 | 113 | // adjusted for scroll position 114 | int top; 115 | int bottom; 116 | }; 117 | 118 | List gui_tracks; 119 | List display_tracks; 120 | int display_track_count; 121 | 122 | MenuWidgetItem *track_context_menu; 123 | GuiTrack *menu_track; 124 | ScrollBarWidget *vert_scroll_bar; 125 | ScrollBarWidget *horiz_scroll_bar; 126 | 127 | bool scrub_mouse_down; 128 | 129 | void update_model(); 130 | void update_play_head_model(); 131 | GuiTrack *create_gui_track(); 132 | DisplayTrack * create_display_track(GuiTrack *gui_track); 133 | DisplayAudioClipSegment * create_display_audio_clip_segment( 134 | DisplayTrack *display_track, GuiAudioClipSegment *gui_audio_clip_segment); 135 | GuiAudioClipSegment * create_gui_audio_clip_segment(); 136 | void destroy_gui_track(GuiTrack *gui_track); 137 | void right_click_track_head(GuiTrack *gui_track, int x, int y); 138 | void clear_track_context_menu(); 139 | GuiTrack *get_track_body_at(int x, int y); 140 | GuiTrack *get_track_head_at(int x, int y); 141 | void on_drag_sample_file(DraggedSampleFile *dragged_sample_file, const DragEvent *event); 142 | void on_drag_audio_asset(AudioAsset *audio_asset, const DragEvent *event); 143 | void on_drag_audio_clip(AudioClip *audio_clip, const DragEvent *event); 144 | void refresh_tracks(); 145 | void destroy_display_track(DisplayTrack *display_track); 146 | void destroy_display_audio_clip_segment(DisplayAudioClipSegment *display_audio_clip_segment); 147 | void destroy_gui_audio_clip_segment(GuiAudioClipSegment *gui_audio_clip_segment); 148 | int whole_note_to_pixel(double whole_note_pos); 149 | double pixel_to_whole_note(int pixel_x); 150 | void scrub(const MouseEvent *event); 151 | }; 152 | 153 | #endif 154 | -------------------------------------------------------------------------------- /src/settings_file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGS_FILE_HPP 2 | #define SETTINGS_FILE_HPP 3 | 4 | #include "byte_buffer.hpp" 5 | #include "string.hpp" 6 | #include "uint256.hpp" 7 | #include "device_id.hpp" 8 | #include "event_dispatcher.hpp" 9 | #include "audio_file.hpp" 10 | 11 | struct LaxJsonContext; 12 | 13 | enum SettingsFileState { 14 | SettingsFileStateStart, 15 | SettingsFileStateEnd, 16 | SettingsFileStateReadyForProp, 17 | SettingsFileStateOpenProjectFile, 18 | SettingsFileStateUserName, 19 | SettingsFileStateUserId, 20 | SettingsFileStateLatency, 21 | SettingsFileStateExpectSampleDirs, 22 | SettingsFileStateSampleDirsItem, 23 | SettingsFileStatePerspectives, 24 | SettingsFileStatePerspectivesItem, 25 | SettingsFileStatePerspectivesItemProp, 26 | SettingsFileStatePerspectivesItemPropName, 27 | SettingsFileStatePerspectivesItemPropDock, 28 | SettingsFileStateDockItemProp, 29 | SettingsFileStateDockItemPropType, 30 | SettingsFileStateDockItemPropSplitRatio, 31 | SettingsFileStateDockItemPropChildA, 32 | SettingsFileStateDockItemPropChildB, 33 | SettingsFileStateDockItemPropTabs, 34 | SettingsFileStateTabName, 35 | SettingsFileStateOpenWindows, 36 | SettingsFileStateOpenWindowItem, 37 | SettingsFileStateOpenWindowItemProp, 38 | SettingsFileStateOpenWindowPerspectiveIndex, 39 | SettingsFileStateOpenWindowLeft, 40 | SettingsFileStateOpenWindowTop, 41 | SettingsFileStateOpenWindowWidth, 42 | SettingsFileStateOpenWindowHeight, 43 | SettingsFileStateOpenWindowMaximized, 44 | SettingsFileStateOpenWindowAlwaysShowTabs, 45 | SettingsFileStateDeviceDesignations, 46 | SettingsFileStateDeviceDesignationProp, 47 | SettingsFileStateDeviceDesignationValue, 48 | SettingsFileStateDeviceDesignationIdProp, 49 | SettingsFileStateDeviceDesignationIdBackend, 50 | SettingsFileStateDeviceDesignationIdDevice, 51 | SettingsFileStateDeviceDesignationIdRaw, 52 | SettingsFileStateDefaultRenderFormat, 53 | SettingsFileStateDefaultRenderParams, 54 | SettingsFileStateDefaultRenderParamsFormat, 55 | SettingsFileStateDefaultRenderParamsReadyForObj, 56 | SettingsFileStateDefaultRenderParamsProp, 57 | SettingsFileStateDefaultRenderParamsSampleFormat, 58 | SettingsFileStateDefaultRenderParamsBitRate, 59 | }; 60 | 61 | enum SettingsFileDockType { 62 | SettingsFileDockTypeTabs, 63 | SettingsFileDockTypeHoriz, 64 | SettingsFileDockTypeVert, 65 | }; 66 | 67 | struct SettingsFileDock { 68 | SettingsFileDockType dock_type; 69 | float split_ratio; 70 | SettingsFileDock *child_a; 71 | SettingsFileDock *child_b; 72 | List tabs; 73 | }; 74 | 75 | struct SettingsFilePerspective { 76 | String name; 77 | SettingsFileDock dock; 78 | }; 79 | 80 | struct SettingsFileOpenWindow { 81 | int perspective_index; 82 | int left; 83 | int top; 84 | int width; 85 | int height; 86 | bool maximized; 87 | bool always_show_tabs; 88 | }; 89 | 90 | struct SettingsFileDeviceId { 91 | SoundIoBackend backend; 92 | ByteBuffer device_id; 93 | bool is_raw; 94 | }; 95 | 96 | struct SettingsFile { 97 | // use this to announce when you change a setting (if anyone cares) 98 | EventDispatcher events; 99 | 100 | // settings you can directly manipulate 101 | uint256 open_project_id; 102 | String user_name; 103 | uint256 user_id; 104 | List open_windows; 105 | List perspectives; 106 | List sample_dirs; 107 | // index is DeviceId. if backend_name is NULL then that DeviceId is unspecified 108 | List device_designations; 109 | double latency; 110 | RenderFormatType default_render_format; 111 | SoundIoFormat default_render_sample_formats[RenderFormatTypeCount]; 112 | int default_render_bit_rates[RenderFormatTypeCount]; 113 | 114 | // private state 115 | ByteBuffer path; 116 | SettingsFileState state; 117 | LaxJsonContext *json; 118 | SettingsFilePerspective *current_perspective; 119 | SettingsFileDock *current_dock; 120 | List dock_stack; 121 | SettingsFileOpenWindow *current_open_window; 122 | DeviceId current_device_id; 123 | SettingsFileDeviceId *current_sf_device_id; 124 | RenderFormatType current_default_render_params_format; 125 | }; 126 | 127 | SettingsFile *settings_file_open(const ByteBuffer &path); 128 | void settings_file_close(SettingsFile *sf); 129 | 130 | // atomically update settings file on disk 131 | int settings_file_commit(SettingsFile *sf); 132 | 133 | void settings_file_clear_dock(SettingsFileDock *dock); 134 | 135 | // you still need to commit after calling these 136 | void settings_file_set_default_render_format(SettingsFile *sf, RenderFormatType format_type); 137 | 138 | void settings_file_set_default_render_sample_format(SettingsFile *sf, 139 | RenderFormatType format_type, SoundIoFormat sample_format); 140 | 141 | void settings_file_set_default_render_bit_rate(SettingsFile *sf, 142 | RenderFormatType format_type, int bit_rate); 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /src/text_widget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_WIDGET_HPP 2 | #define TEXT_WIDGET_HPP 3 | 4 | #include "string.hpp" 5 | #include "label.hpp" 6 | #include "glm.hpp" 7 | #include "gui.hpp" 8 | #include "widget.hpp" 9 | 10 | struct SpritesheetImage; 11 | class GuiWindow; 12 | 13 | class TextWidget : public Widget { 14 | public: 15 | TextWidget(GuiWindow *gui_window); 16 | ~TextWidget() {} 17 | 18 | void draw(const glm::mat4 &projection) override; 19 | 20 | void on_mouse_move(const MouseEvent *event) override; 21 | void on_mouse_out(const MouseEvent *event) override; 22 | void on_mouse_over(const MouseEvent *event) override; 23 | void on_gain_focus() override; 24 | void on_lose_focus() override; 25 | void on_text_input(const TextInputEvent *event) override; 26 | bool on_key_event(const KeyEvent *event) override; 27 | 28 | int min_width() const override; 29 | int max_width() const override; 30 | int min_height() const override; 31 | int max_height() const override; 32 | 33 | // return true if you ate the event 34 | void set_on_key_event(bool (*fn)(TextWidget *, const KeyEvent *event)) { 35 | _on_key_event = fn; 36 | } 37 | 38 | // return true if you ate the event 39 | void set_on_text_change_event(void (*fn)(TextWidget *)) { 40 | _on_text_change_event = fn; 41 | } 42 | 43 | // return true if you ate the event 44 | void set_on_mouse_event(bool (*fn)(TextWidget *, const MouseEvent *event)) { 45 | _on_mouse_event = fn; 46 | } 47 | 48 | void set_text(const String &text); 49 | const String &text() const { 50 | return _label.text(); 51 | } 52 | 53 | void set_placeholder_text(const String &text); 54 | 55 | void set_font_size(int size) { 56 | _label.set_font_size(size); 57 | _label.update(); 58 | update_model(); 59 | } 60 | 61 | void on_resize() override { 62 | scroll_cursor_into_view(); 63 | } 64 | 65 | 66 | 67 | void set_min_width(int width); 68 | void set_max_width(int width); 69 | 70 | void set_auto_size(bool value); 71 | 72 | 73 | void set_selection(int start, int end); 74 | 75 | bool have_focus() { 76 | return _have_focus; 77 | } 78 | 79 | void select_all(); 80 | void cut(); 81 | void copy(); 82 | void paste(); 83 | 84 | void set_background_on(bool value) { 85 | _background_on = value; 86 | } 87 | 88 | void set_hover_on(bool value) { 89 | _hover_on = value; 90 | } 91 | 92 | void set_text_interaction(bool value) { 93 | _text_interaction_on = value; 94 | } 95 | 96 | void set_background_color(const glm::vec4 &color) { 97 | _background_color = color; 98 | } 99 | 100 | void set_hover_color(const glm::vec4 &color) { 101 | _hover_color = color; 102 | } 103 | 104 | void set_icon(const SpritesheetImage *icon); 105 | 106 | 107 | void *_userdata; 108 | 109 | Label _label; 110 | glm::mat4 _label_model; 111 | glm::mat4 _bg_model; 112 | 113 | int _padding_left; 114 | int _padding_right; 115 | int _padding_top; 116 | int _padding_bottom; 117 | glm::vec4 _text_color; 118 | glm::vec4 _sel_text_color; 119 | glm::vec4 _background_color; 120 | glm::vec4 _hover_color; 121 | glm::vec4 _selection_color; 122 | glm::vec4 _cursor_color; 123 | bool _auto_size; 124 | int fixed_min_width; 125 | int fixed_max_width; 126 | int _icon_size_w; 127 | int _icon_size_h; 128 | int _icon_margin; 129 | 130 | int _cursor_start; 131 | int _cursor_end; 132 | bool _select_down; 133 | 134 | glm::mat4 _sel_model; 135 | glm::mat4 _cursor_model; 136 | 137 | bool _have_focus; 138 | 139 | int _scroll_x; 140 | 141 | Label _placeholder_label; 142 | glm::vec4 _placeholder_color; 143 | bool _mouse_down_dbl; 144 | int _dbl_select_start; 145 | int _dbl_select_end; 146 | 147 | bool _background_on; 148 | bool _hover_on; 149 | bool _text_interaction_on; 150 | 151 | const SpritesheetImage *_icon_img; 152 | glm::mat4 _icon_model; 153 | 154 | bool _hovering; 155 | 156 | bool (*_on_key_event)(TextWidget *, const KeyEvent *event); 157 | void (*_on_text_change_event)(TextWidget *); 158 | bool (*_on_mouse_event)(TextWidget *, const MouseEvent *event); 159 | 160 | void update_model(); 161 | 162 | int cursor_at_pos(int x, int y) const; 163 | void pos_at_cursor(int index, int &x, int &y) const; 164 | void get_cursor_slice(int &start, int &end) const; 165 | void update_selection_model(); 166 | void replace_text(int start, int end, const String &text, int cursor_modifier); 167 | int backward_word(); 168 | int forward_word(); 169 | int advance_word(int dir); 170 | int advance_word_from_index(int index, int dir); 171 | void scroll_cursor_into_view(); 172 | void scroll_index_into_view(int char_index); 173 | 174 | int label_start_x() const; 175 | int label_start_y() const; 176 | int label_area_width() const; 177 | 178 | int bg_width() const; 179 | int bg_height() const; 180 | 181 | TextWidget(const TextWidget ©) = delete; 182 | TextWidget &operator=(const TextWidget ©) = delete; 183 | }; 184 | 185 | #endif 186 | -------------------------------------------------------------------------------- /src/list.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIST_HPP 2 | #define LIST_HPP 3 | 4 | #include "util.hpp" 5 | #include "genesis.h" 6 | 7 | #include 8 | 9 | template 10 | class List { 11 | public: 12 | List() { 13 | _length = 0; 14 | _capacity = 0; 15 | _items = NULL; 16 | } 17 | ~List() { 18 | destroy(_items, _capacity); 19 | } 20 | 21 | bool operator==(const List &other) { 22 | if (_length != other._length) 23 | return false; 24 | 25 | for (int i = 0; i < _length; i += 1) { 26 | if (_items[i] != other._items[i]) 27 | return false; 28 | } 29 | 30 | return true; 31 | } 32 | 33 | inline bool operator!=(const List &other) { 34 | return !(*this == other); 35 | } 36 | 37 | int __attribute__((warn_unused_result)) append(T item) { 38 | int err = ensure_capacity(_length + 1); 39 | if (err) 40 | return err; 41 | _items[_length++] = item; 42 | return 0; 43 | } 44 | // remember that the pointer to this item is invalid after you 45 | // modify the length of the list 46 | const T & at(int index) const { 47 | assert(index >= 0); 48 | assert(index < _length); 49 | return _items[index]; 50 | } 51 | T & at(int index) { 52 | assert(index >= 0); 53 | assert(index < _length); 54 | return _items[index]; 55 | } 56 | int length() const { 57 | return _length; 58 | } 59 | T pop() { 60 | assert(_length >= 1); 61 | return unchecked_pop(); 62 | } 63 | 64 | int __attribute__((warn_unused_result)) add_one() { 65 | return resize(_length + 1); 66 | } 67 | 68 | const T & last() const { 69 | assert(_length >= 1); 70 | return _items[_length - 1]; 71 | } 72 | 73 | T & last() { 74 | assert(_length >= 1); 75 | return _items[_length - 1]; 76 | } 77 | 78 | int __attribute__((warn_unused_result)) resize(int length) { 79 | assert(length >= 0); 80 | int err = ensure_capacity(length); 81 | if (err) 82 | return err; 83 | _length = length; 84 | return 0; 85 | } 86 | 87 | T *raw() const { 88 | return _items; 89 | } 90 | 91 | T swap_remove(int index) { 92 | assert(index >= 0); 93 | assert(index < _length); 94 | return unchecked_swap_remove(index); 95 | } 96 | 97 | void remove_range(int start, int end) { 98 | assert(0 <= start); 99 | assert(start <= end); 100 | assert(end <= _length); 101 | int del_count = end - start; 102 | for (int i = start; i < _length - del_count; i += 1) { 103 | _items[i] = _items[i + del_count]; 104 | } 105 | _length -= del_count; 106 | } 107 | 108 | int __attribute__((warn_unused_result)) insert_space(int pos, int size) { 109 | int old_length = _length; 110 | assert(pos >= 0 && pos <= old_length); 111 | int err = resize(old_length + size); 112 | if (err) 113 | return err; 114 | 115 | for (int i = old_length - 1; i >= pos; i -= 1) { 116 | _items[i + size] = _items[i]; 117 | } 118 | 119 | return 0; 120 | } 121 | 122 | void fill(T value) { 123 | for (int i = 0; i < _length; i += 1) { 124 | _items[i] = value; 125 | } 126 | } 127 | 128 | void clear() { 129 | _length = 0; 130 | } 131 | 132 | template 133 | void sort() { 134 | quick_sort(_items, _length); 135 | } 136 | 137 | template 138 | void filter_with_order_undefined(void *context) { 139 | for (int i = 0; i < _length;) { 140 | if (!filter_fn(context, _items[i])) 141 | unchecked_swap_remove(i); 142 | else 143 | i += 1; 144 | } 145 | } 146 | 147 | T unchecked_swap_remove(int index) { 148 | if (index == _length - 1) 149 | return unchecked_pop(); 150 | 151 | T last = unchecked_pop(); 152 | T item = _items[index]; 153 | _items[index] = last; 154 | return item; 155 | } 156 | 157 | T unchecked_pop() { 158 | return _items[--_length]; 159 | } 160 | 161 | int capacity() const { 162 | return _capacity; 163 | } 164 | 165 | int __attribute__((warn_unused_result)) ensure_capacity(int new_capacity) { 166 | int better_capacity = max(_capacity, 16); 167 | while (better_capacity < new_capacity) 168 | better_capacity = better_capacity * 2; 169 | if (better_capacity != _capacity) { 170 | T *new_items = reallocate_safe(_items, _capacity, better_capacity); 171 | if (!new_items) 172 | return GenesisErrorNoMem; 173 | _items = new_items; 174 | _capacity = better_capacity; 175 | } 176 | return 0; 177 | } 178 | 179 | int allocated_size() const { 180 | return _capacity * sizeof(T); 181 | } 182 | 183 | private: 184 | T * _items; 185 | int _length; 186 | int _capacity; 187 | 188 | 189 | List(const List &other) = delete; 190 | List& operator= (const List &other) = delete; 191 | }; 192 | 193 | #endif 194 | --------------------------------------------------------------------------------