├── src ├── contrib │ ├── SimpleLogger │ │ ├── .gitignore │ │ ├── src │ │ │ ├── tests │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── file_test │ │ │ │ │ └── CMakeLists.txt │ │ │ │ └── stream_test │ │ │ │ │ └── CMakeLists.txt │ │ │ └── CMakeLists.txt │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── .clang-format │ │ ├── Jenkinsfile │ │ └── CHANGELOG │ └── Argengine │ │ ├── .gitignore │ │ ├── src │ │ ├── examples │ │ │ ├── CMakeLists.txt │ │ │ ├── ex1 │ │ │ │ └── CMakeLists.txt │ │ │ ├── ex2 │ │ │ │ └── CMakeLists.txt │ │ │ └── ex3 │ │ │ │ └── CMakeLists.txt │ │ ├── tests │ │ │ ├── CMakeLists.txt │ │ │ ├── help_test │ │ │ │ └── CMakeLists.txt │ │ │ ├── valueless_test │ │ │ │ └── CMakeLists.txt │ │ │ ├── option_group_test │ │ │ │ └── CMakeLists.txt │ │ │ ├── single_value_test │ │ │ │ └── CMakeLists.txt │ │ │ ├── unknown_argument_test │ │ │ │ └── CMakeLists.txt │ │ │ ├── conflicting_arguments_test │ │ │ │ └── CMakeLists.txt │ │ │ └── positional_argument_test │ │ │ │ └── CMakeLists.txt │ │ └── CMakeLists.txt │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── .clang-format │ │ ├── Jenkinsfile │ │ └── CHANGELOG ├── view │ └── qml │ │ ├── Editor │ │ ├── Cursor.qml │ │ ├── PositionBar.qml │ │ ├── IndexHighlight.qml │ │ ├── MainContextMenu.qml │ │ ├── LineNumberDelegate.qml │ │ ├── VelocityScale.qml │ │ ├── NoteColumn_LineDelegate.qml │ │ ├── MainContextMenu_Line.qml │ │ └── TrackHeader_ColumnButtons.qml │ │ ├── ToolBar │ │ ├── Separator.qml │ │ ├── PlayerControls.qml │ │ ├── PlayerButtons │ │ │ ├── StopButton.qml │ │ │ ├── PlayButton.qml │ │ │ ├── PrevButton.qml │ │ │ └── LoopButton.qml │ │ └── MainToolBar.qml │ │ ├── Graphics │ │ ├── play.svg │ │ ├── stop.svg │ │ ├── next.svg │ │ ├── prev.svg │ │ ├── pause.svg │ │ ├── add_box.svg │ │ ├── settings.svg │ │ ├── del_box.svg │ │ ├── delete.svg │ │ └── replay.svg │ │ ├── MainMenu │ │ ├── MainMenu.qml │ │ ├── MainMenu_Help.qml │ │ ├── MainMenu_Tools.qml │ │ ├── MainMenu_Edit.qml │ │ └── MainMenu_File.qml │ │ ├── Components │ │ ├── LayoutSeparator.qml │ │ └── MenuItemDelegate.qml │ │ └── Dialogs │ │ ├── EditMidiCcAutomationsDialog.qml │ │ ├── EditPitchBendAutomationsDialog.qml │ │ ├── AddPitchBendAutomationDialog.qml │ │ ├── IntegerInputDialog.qml │ │ ├── UnsavedChangesDialog.qml │ │ ├── TrackSettingsDialog_MidiCcSettings_Custom.qml │ │ ├── ColumnSettingsDialog.qml │ │ ├── GainConverterDialog.qml │ │ ├── AboutDialog.qml │ │ ├── AddMidiCcAutomationDialog.qml │ │ ├── SettingsDialog.qml │ │ └── SettingsDialog_AudioSettings.qml ├── unit_tests │ ├── note_converter_test │ │ ├── CMakeLists.txt │ │ └── note_converter_test.hpp │ ├── play_order_test │ │ ├── CMakeLists.txt │ │ └── play_order_test.hpp │ ├── mixer_service_test │ │ └── CMakeLists.txt │ ├── selection_service_test │ │ ├── CMakeLists.txt │ │ └── selection_service_test.hpp │ ├── column_settings_model_test │ │ ├── CMakeLists.txt │ │ └── column_settings_model_test.hpp │ ├── midi_cc_selection_model_test │ │ ├── CMakeLists.txt │ │ └── midi_cc_selection_model_test.hpp │ ├── CMakeLists.txt │ ├── midi_cc_automations_model_test │ │ ├── CMakeLists.txt │ │ └── midi_cc_automations_model_test.hpp │ ├── pitch_bend_automations_model_test │ │ ├── CMakeLists.txt │ │ └── pitch_bend_automations_model_test.hpp │ ├── track_settings_model_test │ │ ├── CMakeLists.txt │ │ └── track_settings_model_test.hpp │ ├── automation_service_test │ │ ├── CMakeLists.txt │ │ └── automation_service_test.hpp │ ├── side_chain_service_test │ │ ├── side_chain_service_test.hpp │ │ ├── CMakeLists.txt │ │ └── side_chain_service_test.cpp │ ├── midi_exporter_test │ │ ├── midi_exporter_test.hpp │ │ └── CMakeLists.txt │ ├── song_test │ │ └── CMakeLists.txt │ └── editor_service_test │ │ └── CMakeLists.txt ├── domain │ ├── event_data.hpp │ ├── event_data.cpp │ ├── midi_note_data.cpp │ ├── midi_note_data.hpp │ ├── mixer_unit.cpp │ ├── interpolator.hpp │ ├── mixer_unit.hpp │ ├── midi_cc_data.hpp │ ├── midi_cc_data.cpp │ ├── pitch_bend_data.hpp │ ├── note_data_manipulator.hpp │ ├── interpolator.cpp │ ├── automation.hpp │ ├── midi_cc_setting.hpp │ ├── pitch_bend_data.cpp │ ├── line_event.hpp │ ├── play_order.hpp │ ├── side_chain_settings.hpp │ ├── midi_address.hpp │ ├── instrument.hpp │ ├── automation_location.hpp │ ├── automation.cpp │ ├── line.hpp │ ├── column_settings.hpp │ └── note_data.hpp ├── application │ ├── service │ │ ├── random_service.cpp │ │ ├── random_service.hpp │ │ ├── util_service.hpp │ │ ├── audio_worker.hpp │ │ ├── audio_worker.cpp │ │ ├── audio_service.hpp │ │ ├── recent_files_manager.hpp │ │ ├── midi_worker.hpp │ │ └── audio_service.cpp │ ├── note_converter.hpp │ ├── instrument_request.cpp │ ├── ui_logger.hpp │ ├── instrument_request.hpp │ ├── models │ │ ├── recent_files_model.hpp │ │ ├── recent_files_model.cpp │ │ ├── note_column_line_container_helper.hpp │ │ └── midi_cc_selection_model.hpp │ └── ui_logger.cpp ├── infra │ ├── audio │ │ ├── audio_recorder.cpp │ │ └── audio_recorder.hpp │ ├── midi │ │ ├── midi_backend_in.cpp │ │ ├── midi_port.cpp │ │ ├── midi_port.hpp │ │ ├── midi_backend_in.hpp │ │ ├── implementation │ │ │ └── librtmidi │ │ │ │ └── midi_in_rt_midi.hpp │ │ └── midi_backend.hpp │ ├── settings.hpp │ └── video │ │ ├── animation.cpp │ │ ├── video_generator.hpp │ │ ├── bars_animation.hpp │ │ └── video_config.hpp └── common │ └── utils.hpp ├── screenshots ├── Timing.png ├── 0.15.0 │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 2.png │ ├── 2b.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ ├── 9b.png │ └── 15b.png ├── 0.0.1 │ ├── Noteahead_1.png │ ├── Noteahead_2.png │ ├── Noteahead_3.png │ └── Noteahead_4.png ├── 0.2.0 │ ├── Noteahead_1.png │ ├── Noteahead_2.png │ └── Noteahead_3.png ├── 0.6.0 │ ├── Noteahead_1.png │ ├── Noteahead_2.png │ └── Noteahead_3.png └── dev │ ├── Noteahead_01-16-2025.png │ ├── Noteahead_09-11-2024.png │ └── Noteahead_30-01-2025.png ├── data └── linux │ ├── noteahead.png │ ├── noteahead.desktop │ └── noteahead.appdata.xml ├── scripts ├── build-archive └── update-version ├── .gitignore ├── .clang-format └── cmake └── DebianPackagingDefaultQt.cmake /src/contrib/SimpleLogger/.gitignore: -------------------------------------------------------------------------------- 1 | build* 2 | *.user 3 | 4 | -------------------------------------------------------------------------------- /src/contrib/Argengine/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | CMakeLists.txt.user 3 | -------------------------------------------------------------------------------- /screenshots/Timing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/Timing.png -------------------------------------------------------------------------------- /data/linux/noteahead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/data/linux/noteahead.png -------------------------------------------------------------------------------- /screenshots/0.15.0/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/1.png -------------------------------------------------------------------------------- /screenshots/0.15.0/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/10.png -------------------------------------------------------------------------------- /screenshots/0.15.0/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/11.png -------------------------------------------------------------------------------- /screenshots/0.15.0/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/12.png -------------------------------------------------------------------------------- /screenshots/0.15.0/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/13.png -------------------------------------------------------------------------------- /screenshots/0.15.0/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/14.png -------------------------------------------------------------------------------- /screenshots/0.15.0/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/15.png -------------------------------------------------------------------------------- /screenshots/0.15.0/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/16.png -------------------------------------------------------------------------------- /screenshots/0.15.0/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/2.png -------------------------------------------------------------------------------- /screenshots/0.15.0/2b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/2b.png -------------------------------------------------------------------------------- /screenshots/0.15.0/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/3.png -------------------------------------------------------------------------------- /screenshots/0.15.0/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/4.png -------------------------------------------------------------------------------- /screenshots/0.15.0/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/5.png -------------------------------------------------------------------------------- /screenshots/0.15.0/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/6.png -------------------------------------------------------------------------------- /screenshots/0.15.0/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/7.png -------------------------------------------------------------------------------- /screenshots/0.15.0/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/8.png -------------------------------------------------------------------------------- /screenshots/0.15.0/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/9.png -------------------------------------------------------------------------------- /screenshots/0.15.0/9b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/9b.png -------------------------------------------------------------------------------- /screenshots/0.15.0/15b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.15.0/15b.png -------------------------------------------------------------------------------- /scripts/build-archive: -------------------------------------------------------------------------------- 1 | git archive --format=tar.gz --prefix=noteahead-1.1.0/ HEAD > noteahead-1.1.0.tar.gz 2 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/src/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(file_test) 2 | add_subdirectory(stream_test) 3 | -------------------------------------------------------------------------------- /screenshots/0.0.1/Noteahead_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.0.1/Noteahead_1.png -------------------------------------------------------------------------------- /screenshots/0.0.1/Noteahead_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.0.1/Noteahead_2.png -------------------------------------------------------------------------------- /screenshots/0.0.1/Noteahead_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.0.1/Noteahead_3.png -------------------------------------------------------------------------------- /screenshots/0.0.1/Noteahead_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.0.1/Noteahead_4.png -------------------------------------------------------------------------------- /screenshots/0.2.0/Noteahead_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.2.0/Noteahead_1.png -------------------------------------------------------------------------------- /screenshots/0.2.0/Noteahead_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.2.0/Noteahead_2.png -------------------------------------------------------------------------------- /screenshots/0.2.0/Noteahead_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.2.0/Noteahead_3.png -------------------------------------------------------------------------------- /screenshots/0.6.0/Noteahead_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.6.0/Noteahead_1.png -------------------------------------------------------------------------------- /screenshots/0.6.0/Noteahead_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.6.0/Noteahead_2.png -------------------------------------------------------------------------------- /screenshots/0.6.0/Noteahead_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/0.6.0/Noteahead_3.png -------------------------------------------------------------------------------- /src/contrib/Argengine/src/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(ex1) 2 | add_subdirectory(ex2) 3 | add_subdirectory(ex3) 4 | -------------------------------------------------------------------------------- /screenshots/dev/Noteahead_01-16-2025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/dev/Noteahead_01-16-2025.png -------------------------------------------------------------------------------- /screenshots/dev/Noteahead_09-11-2024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/dev/Noteahead_09-11-2024.png -------------------------------------------------------------------------------- /screenshots/dev/Noteahead_30-01-2025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juzzlin/Noteahead/HEAD/screenshots/dev/Noteahead_30-01-2025.png -------------------------------------------------------------------------------- /src/view/qml/Editor/Cursor.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | 3 | Rectangle { 4 | color: "red" 5 | opacity: 0.5 6 | visible: false 7 | } 8 | -------------------------------------------------------------------------------- /src/view/qml/ToolBar/Separator.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import ".." 3 | 4 | Rectangle { 5 | height: parent.height 6 | width: 1 7 | color: Constants.mainToolBarSeparatorColor 8 | } 9 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/stop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/prev.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/view/qml/MainMenu/MainMenu.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | 5 | MenuBar { 6 | MainMenu_File {} 7 | MainMenu_Edit {} 8 | MainMenu_Tools {} 9 | MainMenu_Help {} 10 | } 11 | -------------------------------------------------------------------------------- /data/linux/noteahead.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Noteahead 3 | GenericName=MIDI tracker and sequencer application 4 | Comment=Music creation application 5 | Exec=noteahead 6 | Icon=noteahead 7 | Type=Application 8 | Categories=Audio; 9 | StartupNotify=true 10 | 11 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/pause.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(conflicting_arguments_test) 2 | add_subdirectory(help_test) 3 | add_subdirectory(option_group_test) 4 | add_subdirectory(positional_argument_test) 5 | add_subdirectory(single_value_test) 6 | add_subdirectory(unknown_argument_test) 7 | add_subdirectory(valueless_test) 8 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/examples/ex1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME ex1) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) 8 | add_executable(${NAME} ${SRC}) 9 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 10 | 11 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/examples/ex2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME ex2) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) 8 | add_executable(${NAME} ${SRC}) 9 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 10 | 11 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/examples/ex3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME ex3) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) 8 | add_executable(${NAME} ${SRC}) 9 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 10 | 11 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/add_box.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/view/qml/Components/LayoutSeparator.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Layouts 3 | 4 | Item { 5 | Layout.column: 0 6 | Layout.columnSpan: 9 7 | Layout.fillWidth: true 8 | height: 11 9 | Rectangle { 10 | color: "grey" 11 | height: 1 12 | width: parent.width 13 | anchors.centerIn: parent 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/view/qml/ToolBar/PlayerControls.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import "PlayerButtons" 5 | import ".." 6 | 7 | Row { 8 | id: rootItem 9 | anchors.verticalCenter: parent.verticalCenter 10 | spacing: 5 11 | PlayButton {} 12 | PrevButton {} 13 | StopButton {} 14 | LoopButton {} 15 | } 16 | -------------------------------------------------------------------------------- /src/view/qml/Editor/PositionBar.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import ".." 3 | 4 | Rectangle { 5 | id: rootItem 6 | color: "transparent" 7 | border.width: Constants.positionBarBorderWidth 8 | border.color: Constants.positionBarBorderColor 9 | Rectangle { 10 | anchors.fill: parent 11 | color: Constants.positionBarColor 12 | opacity: Constants.positionBarOpacity 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/tests/help_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME help_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/tests/valueless_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME valueless_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/tests/option_group_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME option_group_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/tests/single_value_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME single_value_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/src/tests/file_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SIMPLE_LOGGER_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${SIMPLE_LOGGER_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME file_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/src/tests/stream_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SIMPLE_LOGGER_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${SIMPLE_LOGGER_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME stream_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/tests/unknown_argument_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME unknown_argument_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/tests/conflicting_arguments_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME conflicting_arguments_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/tests/positional_argument_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ARGENGINE_DIR ${CMAKE_SOURCE_DIR}/src) 2 | include_directories(${ARGENGINE} ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | set(NAME positional_argument_test) 5 | set(SRC ${NAME}.cpp) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) 8 | add_executable(${NAME} ${SRC}) 9 | add_test(${NAME} ${CMAKE_BINARY_DIR}/tests/${NAME}) 10 | target_link_libraries(${NAME} ${LIBRARY_NAME}) 11 | -------------------------------------------------------------------------------- /src/unit_tests/note_converter_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME note_converter_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/note_converter.cpp 7 | ) 8 | qt_add_executable(${NAME} ${SRC}) 9 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 10 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 11 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 12 | -------------------------------------------------------------------------------- /src/view/qml/MainMenu/MainMenu_Help.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import ".." 5 | import "../Components" 6 | 7 | Menu { 8 | title: qsTr("&Help") 9 | Action { 10 | text: qsTr("About") 11 | onTriggered: UiService.requestAboutDialog() 12 | } 13 | Action { 14 | text: qsTr("Shortcuts") 15 | onTriggered: UiService.requestShortcutsDialog() 16 | } 17 | delegate: MenuItemDelegate {} 18 | } 19 | -------------------------------------------------------------------------------- /src/view/qml/ToolBar/PlayerButtons/StopButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import ".." 3 | import "../.." 4 | 5 | ToolBarButtonBase { 6 | id: rootItem 7 | enabled: UiService.isPlaying() 8 | Component.onCompleted: { 9 | setImageSource("../Graphics/stop.svg"); 10 | setScale(1.0); 11 | } 12 | onClicked: { 13 | UiService.requestStop(); 14 | focus = false; 15 | UiService.requestFocusOnEditorView(); 16 | } 17 | toolTipText: qsTr("Stop playing") 18 | } 19 | -------------------------------------------------------------------------------- /src/unit_tests/play_order_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME play_order_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../common/constants.cpp 7 | ../../domain/play_order.cpp 8 | ) 9 | qt_add_executable(${NAME} ${SRC}) 10 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 11 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 12 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 13 | -------------------------------------------------------------------------------- /src/unit_tests/mixer_service_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME mixer_service_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/service/mixer_service.cpp 7 | ../../common/constants.cpp 8 | ) 9 | qt_add_executable(${NAME} ${SRC}) 10 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 11 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 12 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 13 | -------------------------------------------------------------------------------- /src/view/qml/ToolBar/PlayerButtons/PlayButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import ".." 3 | import "../.." 4 | 5 | ToolBarButtonBase { 6 | id: rootItem 7 | enabled: !UiService.isPlaying() 8 | Component.onCompleted: { 9 | setImageSource("../Graphics/play.svg"); 10 | setScale(1.0); 11 | } 12 | onClicked: { 13 | UiService.requestPlay(); 14 | focus = false; 15 | UiService.requestFocusOnEditorView(); 16 | } 17 | toolTipText: qsTr("Start playing from the current position") 18 | } 19 | -------------------------------------------------------------------------------- /src/domain/event_data.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVENT_DATA_HPP 2 | #define EVENT_DATA_HPP 3 | 4 | #include 5 | 6 | namespace noteahead { 7 | 8 | class EventData 9 | { 10 | public: 11 | EventData(size_t track, size_t column); 12 | EventData(); 13 | 14 | size_t track() const; 15 | void setTrack(size_t track); 16 | 17 | size_t column() const; 18 | void setColumn(size_t column); 19 | 20 | private: 21 | size_t m_track = 0; 22 | size_t m_column = 0; 23 | }; 24 | 25 | } // namespace noteahead 26 | 27 | #endif // EVENT_DATA_HPP 28 | -------------------------------------------------------------------------------- /src/unit_tests/selection_service_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME selection_service_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/position.hpp 7 | ../../application/service/selection_service.cpp 8 | ) 9 | qt_add_executable(${NAME} ${SRC}) 10 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 11 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 12 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 13 | -------------------------------------------------------------------------------- /src/view/qml/ToolBar/PlayerButtons/PrevButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import ".." 3 | import "../.." 4 | 5 | ToolBarButtonBase { 6 | id: rootItem 7 | enabled: !UiService.isPlaying() 8 | Component.onCompleted: { 9 | setImageSource("../Graphics/prev.svg"); 10 | setScale(1.0); 11 | } 12 | onClicked: { 13 | UiService.rewindSong(); 14 | focus = false; 15 | UiService.requestFocusOnEditorView(); 16 | } 17 | toolTipText: qsTr("Rewind to the start of the song and reset track settings") 18 | } 19 | -------------------------------------------------------------------------------- /src/view/qml/Components/MenuItemDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | 5 | MenuItem { 6 | id: rootItem 7 | contentItem: Item { 8 | anchors.centerIn: parent 9 | Text { 10 | text: rootItem.text 11 | anchors.left: parent.left 12 | color: "white" 13 | } 14 | Text { 15 | text: rootItem?.action?.shortcut || "" 16 | anchors.right: parent.right 17 | color: "white" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/view/qml/ToolBar/PlayerButtons/LoopButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import ".." 3 | import "../.." 4 | 5 | ToolBarButtonBase { 6 | id: rootItem 7 | enabled: !UiService.isPlaying() 8 | Component.onCompleted: { 9 | setImageSource("../Graphics/replay.svg"); 10 | setScale(0.9); 11 | } 12 | onClicked: { 13 | setToggled(!isToggled()); 14 | UiService.setIsLooping(isToggled()); 15 | focus = false; 16 | UiService.requestFocusOnEditorView(); 17 | } 18 | toolTipText: qsTr("Loop the current pattern") 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | build* 35 | *.user 36 | 37 | # Flatpak 38 | .flatpak 39 | .flatpak-builder 40 | build-dir 41 | 42 | # VS Code 43 | .vscode 44 | 45 | # Act 46 | .secrets -------------------------------------------------------------------------------- /src/domain/event_data.cpp: -------------------------------------------------------------------------------- 1 | #include "event_data.hpp" 2 | 3 | namespace noteahead { 4 | 5 | EventData::EventData(size_t track, size_t column) 6 | : m_track { track } 7 | , m_column { column } 8 | { 9 | } 10 | 11 | EventData::EventData() = default; 12 | 13 | size_t EventData::track() const 14 | { 15 | return m_track; 16 | } 17 | 18 | void EventData::setTrack(size_t track) 19 | { 20 | m_track = track; 21 | } 22 | 23 | size_t EventData::column() const 24 | { 25 | return m_column; 26 | } 27 | 28 | void EventData::setColumn(size_t column) 29 | { 30 | m_column = column; 31 | } 32 | 33 | } // namespace noteahead 34 | -------------------------------------------------------------------------------- /src/unit_tests/column_settings_model_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME column_settings_model_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/models/column_settings_model.cpp 7 | ../../domain/column_settings.cpp 8 | ../../common/constants.cpp 9 | ../../common/utils.cpp 10 | ) 11 | qt_add_executable(${NAME} ${SRC}) 12 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 13 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 14 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 15 | -------------------------------------------------------------------------------- /src/view/qml/MainMenu/MainMenu_Tools.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import ".." 5 | import "../Components" 6 | 7 | Menu { 8 | title: qsTr("&Tools") 9 | Action { 10 | text: qsTr("Delay time calculator") 11 | onTriggered: UiService.requestDelayCalculatorDialog() 12 | } 13 | Action { 14 | text: qsTr("Note frequencies") 15 | onTriggered: UiService.requestNoteFrequencyDialog() 16 | } 17 | Action { 18 | text: qsTr("Gain converter") 19 | onTriggered: UiService.requestGainConverterDialog() 20 | } 21 | delegate: MenuItemDelegate {} 22 | } 23 | -------------------------------------------------------------------------------- /src/unit_tests/midi_cc_selection_model_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME midi_cc_selection_model_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/models/midi_cc_selection_model.cpp 7 | ../../common/constants.cpp 8 | ../../common/utils.cpp 9 | ../../domain/midi_cc_setting.cpp 10 | ../../infra/midi/midi_cc_mapping.cpp 11 | ) 12 | qt_add_executable(${NAME} ${SRC}) 13 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 14 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 15 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 16 | -------------------------------------------------------------------------------- /src/view/qml/Editor/IndexHighlight.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | 3 | Rectangle { 4 | property int index: 0 5 | readonly property int _beatLine1: editorService.linesPerBeat 6 | readonly property int _beatLine2: _beatLine1 % 3 ? _beatLine1 / 2 : _beatLine1 / 3 7 | readonly property int _beatLine3: _beatLine1 % 6 ? _beatLine1 / 4 : _beatLine1 / 6 8 | color: "white" 9 | opacity: { 10 | if (!(index % _beatLine1)) 11 | return 0.25; 12 | if (!(index % _beatLine3) && !(index % _beatLine2)) 13 | return 0.10; 14 | if (!(index % _beatLine3)) 15 | return 0.05; 16 | return 0; 17 | } 18 | visible: opacity > 0 19 | } 20 | -------------------------------------------------------------------------------- /src/unit_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(UNIT_TEST_BASE_DIR ${CMAKE_BINARY_DIR}/unit_tests) 2 | add_subdirectory(automation_service_test) 3 | add_subdirectory(editor_service_test) 4 | add_subdirectory(midi_cc_automations_model_test) 5 | add_subdirectory(midi_cc_selection_model_test) 6 | add_subdirectory(mixer_service_test) 7 | add_subdirectory(note_converter_test) 8 | add_subdirectory(pitch_bend_automations_model_test) 9 | add_subdirectory(play_order_test) 10 | add_subdirectory(selection_service_test) 11 | add_subdirectory(side_chain_service_test) 12 | add_subdirectory(song_test) 13 | add_subdirectory(track_settings_model_test) 14 | add_subdirectory(column_settings_model_test) 15 | add_subdirectory(midi_exporter_test) 16 | -------------------------------------------------------------------------------- /src/unit_tests/midi_cc_automations_model_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME midi_cc_automations_model_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/models/midi_cc_automations_model.cpp 7 | ../../common/constants.cpp 8 | ../../common/utils.cpp 9 | ../../domain/automation.cpp 10 | ../../domain/automation_location.cpp 11 | ../../domain/midi_cc_automation.cpp 12 | ) 13 | qt_add_executable(${NAME} ${SRC}) 14 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 15 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 16 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 17 | -------------------------------------------------------------------------------- /src/unit_tests/pitch_bend_automations_model_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME pitch_bend_automations_model_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/models/pitch_bend_automations_model.cpp 7 | ../../common/constants.cpp 8 | ../../common/utils.cpp 9 | ../../domain/automation.cpp 10 | ../../domain/automation_location.cpp 11 | ../../domain/pitch_bend_automation.cpp 12 | ) 13 | qt_add_executable(${NAME} ${SRC}) 14 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 15 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 16 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 17 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/linux/noteahead.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | noteahead.desktop 4 | CC0-1.0 5 | GPL-3.0 6 | 7 | juzzlin 8 | 9 | Noteahead 10 | A MIDI tracker and sequencer application. 11 | 12 |

13 | Noteahead is an application for MIDI music creation, written in Qt/QML/C++. 14 |

15 |
16 | 17 | 18 | https://github.com/juzzlin/Noteahead 19 | jussi.lind@iki.fi 20 | 21 | 22 | 23 |
24 | -------------------------------------------------------------------------------- /src/view/qml/MainMenu/MainMenu_Edit.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import ".." 5 | import "../Components" 6 | 7 | Menu { 8 | title: qsTr("&Edit") 9 | Action { 10 | text: qsTr("Toggle edit mode") 11 | shortcut: "Esc" 12 | onTriggered: UiService.toggleEditMode() 13 | } 14 | MenuSeparator {} 15 | Action { 16 | text: qsTr("Reset instruments") 17 | onTriggered: applicationService.requestInstrumentReset() 18 | } 19 | Action { 20 | text: qsTr("Stop all notes") 21 | onTriggered: applicationService.requestAllNotesOff() 22 | } 23 | MenuSeparator {} 24 | Action { 25 | text: qsTr("Settings") 26 | onTriggered: UiService.requestSettingsDialog() 27 | } 28 | delegate: MenuItemDelegate {} 29 | } 30 | -------------------------------------------------------------------------------- /src/unit_tests/track_settings_model_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME track_settings_model_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/models/track_settings_model.cpp 7 | ../../application/models/midi_cc_selection_model.cpp 8 | ../../common/constants.cpp 9 | ../../common/utils.cpp 10 | ../../domain/instrument.cpp 11 | ../../domain/instrument_settings.cpp 12 | ../../domain/event_data.cpp 13 | ../../domain/midi_address.cpp 14 | ../../domain/midi_cc_setting.cpp 15 | ../../infra/midi/midi_cc_mapping.cpp 16 | ) 17 | qt_add_executable(${NAME} ${SRC}) 18 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 19 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 20 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 21 | -------------------------------------------------------------------------------- /src/contrib/Argengine/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | cmake_policy(VERSION 3.10) 3 | 4 | project(Argengine) 5 | 6 | option(BUILD_EXAMPLES "Build example apps" ON) 7 | 8 | option(BUILD_TESTS "Build unit tests" ON) 9 | 10 | # Default to release C++ flags if CMAKE_BUILD_TYPE not set 11 | if(NOT CMAKE_BUILD_TYPE) 12 | set(CMAKE_BUILD_TYPE Release CACHE STRING 13 | "Choose the type of build, options are: None Debug Release RelWithDebInfo 14 | MinSizeRel." FORCE) 15 | endif() 16 | 17 | set(CMAKE_CXX_STANDARD 17) 18 | 19 | if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") 21 | endif() 22 | 23 | set(LIBRARY_NAME "Argengine") 24 | 25 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 26 | 27 | if(BUILD_TESTS) 28 | enable_testing() 29 | add_subdirectory(src/tests) 30 | endif() 31 | 32 | add_subdirectory(src) 33 | 34 | -------------------------------------------------------------------------------- /src/view/qml/Editor/MainContextMenu.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.15 2 | import QtQuick.Controls.Universal 2.15 3 | import ".." 4 | import "../Components" 5 | 6 | Menu { 7 | id: rootItem 8 | MainContextMenu_Line {} 9 | MenuSeparator {} 10 | MainContextMenu_Column {} 11 | MenuSeparator {} 12 | MainContextMenu_Track {} 13 | MenuSeparator {} 14 | MainContextMenu_Pattern {} 15 | MenuSeparator {} 16 | MainContextMenu_Selection {} 17 | MenuSeparator {} 18 | Action { 19 | text: qsTr("Edit MIDI CC automations (ALL)") 20 | enabled: !UiService.isPlaying() 21 | onTriggered: UiService.requestEditMidiCcAutomationsDialog() 22 | } 23 | MenuSeparator {} 24 | Action { 25 | text: qsTr("Edit Pitch Bend automations (ALL)") 26 | enabled: !UiService.isPlaying() 27 | onTriggered: UiService.requestEditPitchBendAutomationsDialog() 28 | } 29 | delegate: MenuItemDelegate {} 30 | } 31 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | cmake_policy(VERSION 3.10) 3 | 4 | project(SimpleLogger) 5 | 6 | option(BUILD_TESTS "Build unit tests" ON) 7 | 8 | # Default to release C++ flags if CMAKE_BUILD_TYPE not set 9 | if(NOT CMAKE_BUILD_TYPE) 10 | set(CMAKE_BUILD_TYPE Release CACHE STRING 11 | "Choose the type of build, options are: None Debug Release RelWithDebInfo 12 | MinSizeRel." FORCE) 13 | endif() 14 | 15 | set(CMAKE_CXX_STANDARD 17) 16 | 17 | if(CMAKE_COMPILER_IS_GNUCXX OR MINGW OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") 18 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") 20 | endif() 21 | 22 | set(LIBRARY_NAME "SimpleLogger") 23 | 24 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 25 | 26 | if(BUILD_TESTS) 27 | enable_testing() 28 | add_subdirectory(src/tests) 29 | endif() 30 | 31 | add_subdirectory(src) 32 | 33 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRC simple_logger.cpp) 2 | set(HDR simple_logger.hpp) 3 | 4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | add_library(SimpleLoggerLib OBJECT ${SRC}) 7 | set_property(TARGET SimpleLoggerLib PROPERTY POSITION_INDEPENDENT_CODE 1) 8 | 9 | set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) 10 | 11 | add_library(${LIBRARY_NAME} SHARED $) 12 | set_target_properties(${LIBRARY_NAME} PROPERTIES PUBLIC_HEADER ${HDR}) 13 | install(TARGETS ${LIBRARY_NAME} 14 | ARCHIVE DESTINATION lib 15 | LIBRARY DESTINATION lib 16 | PUBLIC_HEADER DESTINATION include 17 | ) 18 | 19 | set(STATIC_LIBRARY_NAME ${LIBRARY_NAME}_static) 20 | add_library(${STATIC_LIBRARY_NAME} STATIC $) 21 | set_target_properties(${STATIC_LIBRARY_NAME} PROPERTIES PUBLIC_HEADER ${HDR}) 22 | install(TARGETS ${STATIC_LIBRARY_NAME} 23 | ARCHIVE DESTINATION lib 24 | LIBRARY DESTINATION lib 25 | PUBLIC_HEADER DESTINATION include 26 | ) 27 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/EditMidiCcAutomationsDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | import QtQuick.Layouts 6 | import ".." 7 | 8 | Dialog { 9 | id: rootItem 10 | modal: true 11 | standardButtons: Dialog.Ok | Dialog.Cancel 12 | function setTitle(text) { 13 | title = "" + text + ""; 14 | } 15 | contentItem: ScrollView { 16 | ListView { 17 | id: midiCcAutomationsList 18 | model: midiCcAutomationsModel 19 | spacing: 10 20 | clip: true 21 | delegate: EditMidiCcAutomationsDelegate { 22 | width: midiCcAutomationsList.width 23 | height: implicitHeight 24 | } 25 | } 26 | } 27 | Text { 28 | text: qsTr("No MIDI CC automations added") 29 | anchors.centerIn: parent 30 | color: "white" 31 | visible: !midiCcAutomationsList.count 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/EditPitchBendAutomationsDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | import QtQuick.Layouts 6 | import ".." 7 | 8 | Dialog { 9 | id: rootItem 10 | modal: true 11 | standardButtons: Dialog.Ok | Dialog.Cancel 12 | function setTitle(text) { 13 | title = "" + text + ""; 14 | } 15 | contentItem: ScrollView { 16 | ListView { 17 | id: pitchBendAutomationsList 18 | model: pitchBendAutomationsModel 19 | spacing: 10 20 | clip: true 21 | delegate: EditPitchBendAutomationsDelegate { 22 | width: pitchBendAutomationsList.width 23 | height: implicitHeight 24 | } 25 | } 26 | } 27 | Text { 28 | text: qsTr("No pitch bend automations added") 29 | anchors.centerIn: parent 30 | color: "white" 31 | visible: !pitchBendAutomationsList.count 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/unit_tests/automation_service_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME automation_service_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/position.hpp 7 | ../../application/service/automation_service.cpp 8 | ../../application/service/random_service.cpp 9 | ../../common/constants.cpp 10 | ../../common/utils.cpp 11 | ../../domain/automation.cpp 12 | ../../domain/automation_location.cpp 13 | ../../domain/event.cpp 14 | ../../domain/event_data.cpp 15 | ../../domain/interpolator.cpp 16 | ../../domain/midi_cc_automation.cpp 17 | ../../domain/midi_cc_data.cpp 18 | ../../domain/note_data.cpp 19 | ../../domain/pitch_bend_automation.cpp 20 | ../../domain/pitch_bend_data.cpp 21 | ) 22 | qt_add_executable(${NAME} ${SRC}) 23 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 24 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 25 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 26 | -------------------------------------------------------------------------------- /src/application/service/random_service.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "random_service.hpp" 17 | 18 | namespace noteahead { 19 | 20 | RandomService::GeneratorR RandomService::generator() 21 | { 22 | static Generator gen { 0 }; // fixed seed for determinism 23 | return gen; 24 | } 25 | 26 | } // namespace noteahead 27 | -------------------------------------------------------------------------------- /src/contrib/Argengine/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SRC argengine.cpp) 2 | set(HDR argengine.hpp) 3 | 4 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | add_library(ArgengineLib OBJECT ${HDR} ${SRC}) 7 | set_property(TARGET ArgengineLib PROPERTY POSITION_INDEPENDENT_CODE 1) 8 | 9 | set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) 10 | 11 | add_library(${LIBRARY_NAME} SHARED $) 12 | set_target_properties(${LIBRARY_NAME} PROPERTIES PUBLIC_HEADER ${HDR}) 13 | install(TARGETS ${LIBRARY_NAME} 14 | ARCHIVE DESTINATION lib 15 | LIBRARY DESTINATION lib 16 | PUBLIC_HEADER DESTINATION include 17 | ) 18 | 19 | set(STATIC_LIBRARY_NAME ${LIBRARY_NAME}_static) 20 | add_library(${STATIC_LIBRARY_NAME} STATIC $) 21 | set_target_properties(${STATIC_LIBRARY_NAME} PROPERTIES PUBLIC_HEADER ${HDR}) 22 | install(TARGETS ${STATIC_LIBRARY_NAME} 23 | ARCHIVE DESTINATION lib 24 | LIBRARY DESTINATION lib 25 | PUBLIC_HEADER DESTINATION include 26 | ) 27 | 28 | if(BUILD_EXAMPLES) 29 | add_subdirectory(examples) 30 | endif() 31 | 32 | -------------------------------------------------------------------------------- /src/application/note_converter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOTE_CONVERTER_HPP 2 | #define NOTE_CONVERTER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace noteahead::NoteConverter { 10 | 11 | //! Converts a MIDI note to a string in "key-octave" format, e.g. "C-3". 12 | std::string midiToString(uint8_t midiNote); 13 | 14 | //! Converts a string in "key-octave" format to a MIDI note [0..127]. 15 | uint8_t stringToMidi(const std::string & noteString); 16 | 17 | //! Converts a MIDI note to key [1..12 ] and octave [0..10] pair. 18 | std::pair midiToKeyAndOctave(uint8_t midiNote); 19 | 20 | using MidiNoteNameAndCode = std::pair; 21 | using MidiNoteNameAndCodeOpt = std::optional; 22 | //! Converts key [1..12] and octave to a MIDI note [0..127] and "key-octave" code pair optional. 23 | //! \returns a nullopt on invalid key. 24 | MidiNoteNameAndCodeOpt keyAndOctaveToMidiNote(uint8_t key, uint8_t octave, int transpose = 0); 25 | 26 | } // namespace noteahead::NoteConverter 27 | 28 | #endif // NOTE_CONVERTER_HPP 29 | -------------------------------------------------------------------------------- /src/infra/audio/audio_recorder.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "audio_recorder.hpp" 17 | 18 | namespace noteahead { 19 | 20 | AudioRecorder::AudioRecorder() = default; 21 | 22 | AudioRecorder::~AudioRecorder() = default; 23 | 24 | void AudioRecorder::start(const std::string &, uint32_t) 25 | { 26 | } 27 | 28 | void AudioRecorder::stop() 29 | { 30 | } 31 | 32 | } // namespace noteahead 33 | -------------------------------------------------------------------------------- /src/infra/midi/midi_backend_in.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "midi_backend_in.hpp" 17 | 18 | namespace noteahead { 19 | 20 | MidiBackendIn::MidiBackendIn() = default; 21 | 22 | void MidiBackendIn::setCallbackForPort(const MidiPort &, InputCallback) 23 | { 24 | } 25 | 26 | void MidiBackendIn::clearCallbacks() 27 | { 28 | } 29 | 30 | MidiBackendIn::~MidiBackendIn() = default; 31 | 32 | } // namespace noteahead 33 | -------------------------------------------------------------------------------- /src/application/service/random_service.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef RANDOM_SERVICE_HPP 17 | #define RANDOM_SERVICE_HPP 18 | 19 | #include 20 | 21 | namespace noteahead::RandomService { 22 | // Deterministic PRNG (shared across events) 23 | using Generator = std::mt19937; 24 | using GeneratorR = Generator &; 25 | GeneratorR generator(); 26 | } // namespace noteahead::RandomService 27 | 28 | #endif // RANDOM_SERVICE_HPP 29 | -------------------------------------------------------------------------------- /src/view/qml/Editor/LineNumberDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import ".." 3 | 4 | Rectangle { 5 | color: lineNumber < 0 ? "transparent" : Constants.lineNumberColumnCellBackgroundColor 6 | border.color: Constants.lineNumberColumnCellBorderColor 7 | border.width: 1 8 | property int index 9 | property int lineNumber 10 | function updateLineNumber() { 11 | lineNumber = editorService.lineNumberAtViewLine(index); 12 | } 13 | function _formattedLineNumber() { 14 | const lineCount = editorService.currentLineCount; 15 | const formattedLineNumber = Math.abs(lineNumber) % lineCount; 16 | return formattedLineNumber < 10 ? `0${formattedLineNumber}` : formattedLineNumber; 17 | } 18 | Text { 19 | color: lineNumber < 0 || lineNumber >= editorService.currentLineCount ? Constants.lineNumberColumnOverflowTextColor : Constants.lineNumberColumnTextColor 20 | font.pixelSize: parent.height * 0.8 21 | font.family: "monospace" 22 | text: _formattedLineNumber() 23 | anchors.centerIn: parent 24 | } 25 | IndexHighlight { 26 | anchors.fill: parent 27 | index: lineNumber 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/domain/midi_note_data.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "midi_note_data.hpp" 17 | 18 | namespace noteahead { 19 | 20 | MidiNoteData::MidiNoteData(uint8_t note, uint8_t velocity) 21 | : m_note { note } 22 | , m_velocity { velocity } 23 | { 24 | } 25 | 26 | uint8_t MidiNoteData::note() const 27 | { 28 | return m_note; 29 | } 30 | 31 | uint8_t MidiNoteData::velocity() const 32 | { 33 | return m_velocity; 34 | } 35 | 36 | } // namespace noteahead 37 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -4 2 | AlignAfterOpenBracket: true 3 | AllowShortFunctionsOnASingleLine: false 4 | AlwaysBreakTemplateDeclarations: true 5 | BasedOnStyle: WebKit 6 | BreakBeforeBinaryOperators: NonAssignment 7 | BreakBeforeBraces: Custom 8 | BreakConstructorInitializersBeforeComma: false 9 | BreakConstructorInitializers: BeforeComma 10 | BraceWrapping: 11 | AfterClass: true 12 | AfterControlStatement: false 13 | AfterEnum: true 14 | AfterFunction: true 15 | AfterNamespace: false 16 | AfterObjCDeclaration: false 17 | AfterStruct: true 18 | AfterUnion: false 19 | BeforeCatch: false 20 | BeforeElse: false 21 | IndentBraces: false 22 | ColumnLimit: 0 23 | CommentPragmas: "^!|^:" 24 | CompactNamespaces: false 25 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 26 | ConstructorInitializerIndentWidth: 2 27 | ContinuationIndentWidth: 2 28 | FixNamespaceComments: true 29 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] 30 | IndentWidth: 4 31 | NamespaceIndentation: None 32 | PointerAlignment: Middle 33 | PointerBindsToType: false 34 | SortIncludes: true 35 | SpaceAfterTemplateKeyword: false 36 | Standard: Cpp11 37 | 38 | -------------------------------------------------------------------------------- /src/contrib/Argengine/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jussi Lind 4 | 5 | https://github.com/juzzlin/Argengine 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jussi Lind 4 | 5 | https://github.com/juzzlin/SimpleLogger 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/contrib/Argengine/.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -4 2 | AlignAfterOpenBracket: true 3 | AllowShortFunctionsOnASingleLine: false 4 | AlwaysBreakTemplateDeclarations: true 5 | BasedOnStyle: WebKit 6 | BreakBeforeBinaryOperators: NonAssignment 7 | BreakBeforeBraces: Custom 8 | BreakConstructorInitializersBeforeComma: false 9 | BreakConstructorInitializers: BeforeComma 10 | BraceWrapping: 11 | AfterClass: true 12 | AfterControlStatement: false 13 | AfterEnum: true 14 | AfterFunction: true 15 | AfterNamespace: false 16 | AfterObjCDeclaration: false 17 | AfterStruct: true 18 | AfterUnion: false 19 | BeforeCatch: false 20 | BeforeElse: false 21 | IndentBraces: false 22 | ColumnLimit: 0 23 | CommentPragmas: "^!|^:" 24 | CompactNamespaces: false 25 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 26 | ConstructorInitializerIndentWidth: 2 27 | ContinuationIndentWidth: 2 28 | FixNamespaceComments: true 29 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] 30 | IndentWidth: 4 31 | NamespaceIndentation: None 32 | PointerAlignment: Middle 33 | PointerBindsToType: false 34 | SortIncludes: true 35 | SpaceAfterTemplateKeyword: false 36 | Standard: Cpp11 37 | 38 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -4 2 | AlignAfterOpenBracket: true 3 | AllowShortFunctionsOnASingleLine: false 4 | AlwaysBreakTemplateDeclarations: true 5 | BasedOnStyle: WebKit 6 | BreakBeforeBinaryOperators: NonAssignment 7 | BreakBeforeBraces: Custom 8 | BreakConstructorInitializersBeforeComma: false 9 | BreakConstructorInitializers: BeforeComma 10 | BraceWrapping: 11 | AfterClass: true 12 | AfterControlStatement: false 13 | AfterEnum: true 14 | AfterFunction: true 15 | AfterNamespace: false 16 | AfterObjCDeclaration: false 17 | AfterStruct: true 18 | AfterUnion: false 19 | BeforeCatch: false 20 | BeforeElse: false 21 | IndentBraces: false 22 | ColumnLimit: 0 23 | CommentPragmas: "^!|^:" 24 | CompactNamespaces: false 25 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 26 | ConstructorInitializerIndentWidth: 2 27 | ContinuationIndentWidth: 2 28 | FixNamespaceComments: true 29 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] 30 | IndentWidth: 4 31 | NamespaceIndentation: None 32 | PointerAlignment: Middle 33 | PointerBindsToType: false 34 | SortIncludes: true 35 | SpaceAfterTemplateKeyword: false 36 | Standard: Cpp11 37 | 38 | -------------------------------------------------------------------------------- /src/domain/midi_note_data.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_NOTE_DATA_HPP 17 | #define MIDI_NOTE_DATA_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class MidiNoteData 24 | { 25 | public: 26 | MidiNoteData(uint8_t note, uint8_t velocity); 27 | 28 | uint8_t note() const; 29 | uint8_t velocity() const; 30 | 31 | private: 32 | uint8_t m_note = 0; 33 | uint8_t m_velocity = 0; 34 | }; 35 | 36 | } // namespace noteahead 37 | 38 | #endif // MIDI_NOTE_DATA_HPP 39 | -------------------------------------------------------------------------------- /src/unit_tests/side_chain_service_test/side_chain_service_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef SIDE_CHAIN_SERVICE_TEST_HPP 17 | #define SIDE_CHAIN_SERVICE_TEST_HPP 18 | 19 | #include 20 | #include 21 | 22 | namespace noteahead { 23 | 24 | class SideChainServiceTest : public QObject 25 | { 26 | Q_OBJECT 27 | 28 | private slots: 29 | void test_setAndGet_sideChainSettings_shouldUpdateModel(); 30 | }; 31 | 32 | } // namespace noteahead 33 | 34 | #endif // SIDE_CHAIN_SERVICE_TEST_HPP 35 | -------------------------------------------------------------------------------- /src/infra/audio/audio_recorder.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef AUDIO_RECORDER_HPP 17 | #define AUDIO_RECORDER_HPP 18 | 19 | #include 20 | #include 21 | 22 | namespace noteahead { 23 | 24 | class AudioRecorder 25 | { 26 | public: 27 | explicit AudioRecorder(); 28 | virtual ~AudioRecorder(); 29 | 30 | virtual void start(const std::string & fileName, uint32_t bufferSize); 31 | virtual void stop(); 32 | }; 33 | 34 | } // namespace noteahead 35 | 36 | #endif // AUDIO_RECORDER_HPP 37 | -------------------------------------------------------------------------------- /scripts/update-version: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Updates version in all build and packaging scripts. 4 | # Must be run in the project root. 5 | 6 | VERSION_MAJOR=$1 7 | VERSION_MINOR=$2 8 | VERSION_PATCH=$3 9 | 10 | DATE_ISO=$(date -u +%Y-%m-%d) 11 | 12 | if [[ ! $1 || ! $2 || ! $3 ]]; then 13 | echo "Usage: $0 VERSION_MAJOR VERSION_MINOR VERSION_PATCH" 14 | exit 1 15 | fi 16 | 17 | 18 | FILE=CMakeLists.txt 19 | echo "Updating ${FILE} .." 20 | sed -i "s/^set(VERSION_MAJOR.*/set(VERSION_MAJOR ${VERSION_MAJOR})/" ${FILE} || exit 1 21 | sed -i "s/^set(VERSION_MINOR.*/set(VERSION_MINOR ${VERSION_MINOR})/" ${FILE} || exit 1 22 | sed -i "s/^set(VERSION_PATCH.*/set(VERSION_PATCH ${VERSION_PATCH})/" ${FILE} || exit 1 23 | 24 | for FILE in scripts/build-archive; do 25 | echo "Updating ${FILE} .." 26 | sed -i -E "s/[0-9]+\.[0-9]+\.[0-9]+/$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH/g" ${FILE} || exit 1 27 | done 28 | 29 | APPDATA_FILE="data/linux/noteahead.appdata.xml" 30 | if [[ -f $APPDATA_FILE ]]; then 31 | echo "Updating ${APPDATA_FILE} .." 32 | sed -i -E "s/( 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "mixer_unit.hpp" 17 | 18 | namespace noteahead { 19 | 20 | MixerUnit::MixerUnit(size_t index, std::string name) 21 | : m_index { index } 22 | , m_name { name } 23 | { 24 | } 25 | 26 | size_t MixerUnit::index() const 27 | { 28 | return m_index; 29 | } 30 | 31 | std::string MixerUnit::name() const 32 | { 33 | return m_name; 34 | } 35 | 36 | void MixerUnit::setName(const std::string & name) 37 | { 38 | m_name = name; 39 | } 40 | 41 | MixerUnit::~MixerUnit() = default; 42 | 43 | } // namespace noteahead 44 | -------------------------------------------------------------------------------- /src/infra/midi/midi_port.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "midi_port.hpp" 17 | 18 | namespace noteahead { 19 | 20 | MidiPort::MidiPort(size_t portIndex, const std::string & portName) 21 | : mIndex { portIndex } 22 | , mName { portName } 23 | { 24 | } 25 | 26 | size_t MidiPort::index() const 27 | { 28 | return mIndex; 29 | } 30 | 31 | const std::string & MidiPort::name() const 32 | { 33 | return mName; 34 | } 35 | 36 | std::string MidiPort::toString() const 37 | { 38 | return std::to_string(mIndex) + ": " + mName; 39 | } 40 | 41 | } // namespace noteahead 42 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/AddPitchBendAutomationDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | import QtQuick.Layouts 6 | 7 | Dialog { 8 | id: rootItem 9 | modal: true 10 | standardButtons: Dialog.Ok | Dialog.Cancel 11 | function setTitle(text) { 12 | title = "" + text + ""; 13 | } 14 | function startValue() { 15 | return model.startValue(); 16 | } 17 | function setStartValue(value) { 18 | model.setStartValue(value); 19 | } 20 | function endValue(value) { 21 | return model.endValue(); 22 | } 23 | function setEndValue(value) { 24 | model.setEndValue(value); 25 | } 26 | function startLine() { 27 | return model.startLine(); 28 | } 29 | function setStartLine(value) { 30 | model.setStartLine(value); 31 | } 32 | function endLine() { 33 | return model.endLine(); 34 | } 35 | function setEndLine(value) { 36 | model.setEndLine(value); 37 | } 38 | function comment() { 39 | return model.comment(); 40 | } 41 | function setComment(comment) { 42 | model.setComment(comment); 43 | } 44 | contentItem: PitchBendAutomationModel { 45 | id: model 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/domain/interpolator.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef INTERPOLATOR_HPP 17 | #define INTERPOLATOR_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class Interpolator 24 | { 25 | public: 26 | Interpolator(size_t startLine, size_t endLine, double startValue, double endValue); 27 | 28 | double getValue(size_t line) const; 29 | 30 | private: 31 | size_t m_startLine = 0; 32 | size_t m_endLine = 0; 33 | 34 | double m_startValue = 0; 35 | double m_endValue = 0; 36 | }; 37 | 38 | } // namespace noteahead 39 | 40 | #endif // INTERPOLATOR_HPP 41 | -------------------------------------------------------------------------------- /src/domain/mixer_unit.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIXER_UNIT_HPP 17 | #define MIXER_UNIT_HPP 18 | 19 | #include 20 | #include 21 | 22 | namespace noteahead { 23 | 24 | class MixerUnit 25 | { 26 | public: 27 | MixerUnit(size_t index, std::string name); 28 | 29 | virtual ~MixerUnit(); 30 | 31 | size_t index() const; 32 | 33 | std::string name() const; 34 | virtual void setName(const std::string & name); 35 | 36 | private: 37 | size_t m_index = 0; 38 | 39 | std::string m_name; 40 | }; 41 | 42 | } // namespace noteahead 43 | 44 | #endif // MIXER_UNIT_HPP 45 | -------------------------------------------------------------------------------- /src/application/service/util_service.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef UTIL_SERVICE_HPP 17 | #define UTIL_SERVICE_HPP 18 | 19 | #include 20 | #include 21 | 22 | namespace noteahead { 23 | 24 | class UtilService : public QObject 25 | { 26 | Q_OBJECT 27 | 28 | public: 29 | Q_INVOKABLE QColor blendColors(QColor a, QColor b, double blendFactor) const; 30 | Q_INVOKABLE QColor scaledColor(QColor color, double scale) const; 31 | 32 | Q_INVOKABLE double indexHighlightOpacity(int index, int linesPerBeat) const; 33 | }; 34 | 35 | } // namespace noteahead 36 | 37 | #endif // UTIL_SERVICE_HPP 38 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/IntegerInputDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | import QtQuick.Layouts 6 | 7 | Dialog { 8 | id: rootItem 9 | modal: true 10 | standardButtons: Dialog.Ok | Dialog.Cancel 11 | function setTitle(text) { 12 | title = "" + text + ""; 13 | } 14 | function setMinValue(value) { 15 | spinBox.from = value; 16 | } 17 | function setMaxValue(value) { 18 | spinBox.to = value; 19 | } 20 | function setValue(value) { 21 | spinBox.value = value; 22 | } 23 | function value() { 24 | return spinBox.value; 25 | } 26 | contentItem: RowLayout { 27 | spacing: 10 28 | width: parent.width 29 | Label { 30 | text: qsTr("Choose a value (%1-%2):").arg(spinBox.from).arg(spinBox.to) 31 | width: parent.width 32 | } 33 | SpinBox { 34 | id: spinBox 35 | width: parent.width * 0.6 36 | from: 0 37 | to: 100 38 | value: 50 39 | editable: true 40 | Keys.onReturnPressed: { 41 | focus = false; 42 | rootItem.accept(); 43 | } 44 | Layout.fillWidth: true 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/application/instrument_request.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "instrument_request.hpp" 17 | 18 | namespace noteahead { 19 | 20 | InstrumentRequest::InstrumentRequest() 21 | : m_instrument { {} } 22 | { 23 | } 24 | 25 | InstrumentRequest::InstrumentRequest(Type type, const Instrument & instrument) 26 | : m_type { type } 27 | , m_instrument { instrument } 28 | { 29 | } 30 | 31 | InstrumentRequest::Type InstrumentRequest::type() const 32 | { 33 | return m_type; 34 | } 35 | 36 | const Instrument & InstrumentRequest::instrument() const 37 | { 38 | return m_instrument; 39 | } 40 | 41 | } // namespace noteahead 42 | -------------------------------------------------------------------------------- /src/domain/midi_cc_data.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_CC_DATA_HPP 17 | #define MIDI_CC_DATA_HPP 18 | 19 | #include "event_data.hpp" 20 | 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | class MidiCcData : public EventData 26 | { 27 | public: 28 | MidiCcData(size_t track, size_t column, uint8_t controller, uint8_t value); 29 | 30 | uint8_t controller() const; 31 | uint8_t value() const; 32 | 33 | double normalizedValue() const; 34 | 35 | private: 36 | uint8_t m_controller = 0; 37 | uint8_t m_value = 0; 38 | }; 39 | 40 | } // namespace noteahead 41 | 42 | #endif // MIDI_CC_DATA_HPP 43 | -------------------------------------------------------------------------------- /src/domain/midi_cc_data.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "midi_cc_data.hpp" 17 | 18 | namespace noteahead { 19 | 20 | MidiCcData::MidiCcData(size_t track, size_t column, uint8_t controller, uint8_t value) 21 | : EventData { track, column } 22 | , m_controller { controller } 23 | , m_value { value } 24 | { 25 | } 26 | 27 | uint8_t MidiCcData::controller() const 28 | { 29 | return m_controller; 30 | } 31 | 32 | uint8_t MidiCcData::value() const 33 | { 34 | return m_value; 35 | } 36 | 37 | double MidiCcData::normalizedValue() const 38 | { 39 | return static_cast(m_value) / 127; 40 | } 41 | 42 | } // namespace noteahead 43 | -------------------------------------------------------------------------------- /src/contrib/Argengine/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent none 3 | stages { 4 | stage('Matrix Build') { 5 | matrix { 6 | axes { 7 | axis { 8 | name 'IMAGE' 9 | values 'qt5-20.04', 'qt6-22.04', 'qt6-24.04' 10 | } 11 | axis { 12 | name 'BUILD_TYPE' 13 | values 'Debug', 'Release' 14 | } 15 | } 16 | stages { 17 | stage('Build and Test') { 18 | agent any 19 | steps { 20 | script { 21 | docker.image("juzzlin/${IMAGE}:latest").inside('--privileged -t -v $WORKSPACE:/Argengine') { 22 | def buildDir = "build-${BUILD_TYPE.toLowerCase()}-${IMAGE}" 23 | sh "mkdir -p ${buildDir}" 24 | sh "cd ${buildDir} && cmake -GNinja -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .." 25 | sh "cd ${buildDir} && cmake --build . && ctest" 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent none 3 | stages { 4 | stage('Matrix Build') { 5 | matrix { 6 | axes { 7 | axis { 8 | name 'IMAGE' 9 | values 'qt5-20.04', 'qt6-22.04', 'qt6-24.04' 10 | } 11 | axis { 12 | name 'BUILD_TYPE' 13 | values 'Debug', 'Release' 14 | } 15 | } 16 | stages { 17 | stage('Build and Test') { 18 | agent any 19 | steps { 20 | script { 21 | docker.image("juzzlin/${IMAGE}:latest").inside('--privileged -t -v $WORKSPACE:/Argengine') { 22 | def buildDir = "build-${BUILD_TYPE.toLowerCase()}-${IMAGE}" 23 | sh "mkdir -p ${buildDir}" 24 | sh "cd ${buildDir} && cmake -GNinja -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .." 25 | sh "cd ${buildDir} && cmake --build . && ctest" 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/infra/midi/midi_port.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_PORT_HPP 17 | #define MIDI_PORT_HPP 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | class MidiPort 26 | { 27 | public: 28 | MidiPort(size_t index, const std::string & name); 29 | 30 | size_t index() const; 31 | const std::string & name() const; 32 | 33 | std::string toString() const; 34 | 35 | private: 36 | size_t mIndex; 37 | std::string mName; 38 | }; 39 | 40 | using MidiPortS = std::shared_ptr; 41 | using MidiPortW = std::weak_ptr; 42 | 43 | } // namespace noteahead 44 | 45 | #endif // MIDI_PORT_HPP 46 | -------------------------------------------------------------------------------- /src/application/service/audio_worker.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef AUDIO_WORKER_HPP 17 | #define AUDIO_WORKER_HPP 18 | 19 | #include 20 | 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | class AudioRecorder; 26 | 27 | class AudioWorker : public QObject 28 | { 29 | Q_OBJECT 30 | 31 | public: 32 | AudioWorker(QObject * parent = nullptr); 33 | ~AudioWorker() override; 34 | 35 | Q_INVOKABLE void startRecording(QString filePath, quint32 bufferSize); 36 | Q_INVOKABLE void stopRecording(); 37 | 38 | private: 39 | std::unique_ptr m_audioRecorder; 40 | }; 41 | 42 | } // namespace noteahead 43 | 44 | #endif // AUDIO_WORKER_HPP 45 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/del_box.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /src/application/service/audio_worker.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "audio_worker.hpp" 17 | 18 | #include "../../infra/audio/implementation/librtaudio/audio_recorder_rt_audio.hpp" 19 | 20 | namespace noteahead { 21 | 22 | AudioWorker::AudioWorker(QObject * parent) 23 | : QObject { parent } 24 | , m_audioRecorder { std::make_unique() } 25 | { 26 | } 27 | 28 | void AudioWorker::startRecording(QString filePath, quint32 bufferSize) 29 | { 30 | m_audioRecorder->start(filePath.toStdString(), bufferSize); 31 | } 32 | 33 | void AudioWorker::stopRecording() 34 | { 35 | m_audioRecorder->stop(); 36 | } 37 | 38 | AudioWorker::~AudioWorker() = default; 39 | 40 | } // namespace noteahead 41 | -------------------------------------------------------------------------------- /src/contrib/SimpleLogger/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2.0.0 2 | ===== 3 | 4 | New features: 5 | 6 | * Fix GitHub Issue #5: Add support for tags 7 | * Add support for customized timestamp format 8 | * Add ISODateTime option to TimestampMode 9 | 10 | Other: 11 | 12 | * Add SimpleLogger::setTimestampSeparator() 13 | * Rename init() to initialize() 14 | * Rename Logger to SimpleLogger 15 | * Require C++17 16 | 17 | 1.4.0 18 | ===== 19 | 20 | New features: 21 | 22 | * Fix GitHub Issue #1: Make the streams configurable 23 | * Add getter for version 24 | 25 | Other: 26 | 27 | * Use std::recursive_mutex 28 | 29 | 1.3.1 30 | ===== 31 | 32 | Bug fixes: 33 | 34 | * Explicitly include chrono 35 | 36 | 1.3.0 37 | ===== 38 | 39 | New features: 40 | 41 | * Fix GitHub Issue #6: Add option to use a microsecond timestamp 42 | - More generic setTimestampMode() replaces enableDateTime() 43 | 44 | 1.2.0 - "Thread-safe Taco" 45 | ========================== 46 | 47 | New features: 48 | 49 | * Make logging thread-safe 50 | 51 | Other changes: 52 | 53 | * Add simple hello world test 54 | 55 | 1.1.0 - "Rename Renegade" 56 | ========================= 57 | 58 | Other changes: 59 | 60 | * Rename src/logger.* to src/simple_logger.* 61 | 62 | 1.0.0 - "Basic Beast" 63 | ===================== 64 | 65 | New features: 66 | 67 | * Based on RAII 68 | * Configurable level symbols 69 | * Date / time 70 | * Logging levels: Trace, Debug, Info, Warning, Error, Fatal 71 | * Log to file and/or console 72 | 73 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/UnsavedChangesDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | 6 | Dialog { 7 | id: rootItem 8 | title: qsTr("Save unsaved changes?") 9 | modal: true 10 | readonly property string _tag: "UnsavedChangesDialog" 11 | footer: DialogButtonBox { 12 | Button { 13 | text: qsTr("Yes") 14 | DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole 15 | onClicked: { 16 | uiLogger.info(_tag, "Unsaved changes accepted"); 17 | applicationService.acceptUnsavedChangesDialog(); 18 | close(); 19 | } 20 | } 21 | Button { 22 | text: qsTr("Close without saving") 23 | DialogButtonBox.buttonRole: DialogButtonBox.NoRole 24 | onClicked: { 25 | uiLogger.info(_tag, "Unsaved changes discarded"); 26 | applicationService.discardUnsavedChangesDialog(); 27 | close(); 28 | } 29 | } 30 | Button { 31 | text: qsTr("Cancel") 32 | DialogButtonBox.buttonRole: DialogButtonBox.RejectRole 33 | onClicked: { 34 | uiLogger.info(_tag, "Unsaved changes rejected"); 35 | applicationService.rejectUnsavedChangesDialog(); 36 | close(); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/infra/midi/midi_backend_in.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_BACKEND_IN_HPP 17 | #define MIDI_BACKEND_IN_HPP 18 | 19 | #include "midi_backend.hpp" 20 | 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | //! Base class for MIDI input backend implementations. 26 | class MidiBackendIn : public MidiBackend 27 | { 28 | public: 29 | MidiBackendIn(); 30 | virtual ~MidiBackendIn() override; 31 | 32 | using InputCallback = std::function &)>; 33 | virtual void setCallbackForPort(const MidiPort & port, InputCallback callback); 34 | virtual void clearCallbacks(); 35 | }; 36 | 37 | } // namespace noteahead 38 | 39 | #endif // MIDI_BACKEND_IN_HPP 40 | -------------------------------------------------------------------------------- /src/view/qml/ToolBar/MainToolBar.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import ".." 5 | 6 | Rectangle { 7 | id: rootItem 8 | height: editorControlsContainer.height 9 | gradient: Gradient { 10 | GradientStop { 11 | position: 0.0 12 | color: Constants.mainToolBarGradientStartColor 13 | } 14 | GradientStop { 15 | position: 1.0 16 | color: Constants.mainToolBarGradientStopColor 17 | } 18 | } 19 | ScrollView { 20 | id: editorControlsContainer 21 | anchors.left: parent.left 22 | anchors.leftMargin: Constants.lineNumberColumnWidth 23 | anchors.right: parent.right 24 | anchors.rightMargin: Constants.lineNumberColumnWidth 25 | anchors.top: parent.top 26 | height: editorControlsWrapper.height 27 | clip: true 28 | ScrollBar.vertical.policy: ScrollBar.AlwaysOff 29 | contentWidth: editorControls.width 30 | // Wrapper to allow vertical centering of content 31 | Item { 32 | id: editorControlsWrapper 33 | width: rootItem.width 34 | height: editorControls.height + 40 35 | anchors.verticalCenter: parent.verticalCenter 36 | EditorControls { 37 | id: editorControls 38 | anchors.verticalCenter: parent.verticalCenter 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/application/service/audio_service.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef AUDIO_SERVICE_HPP 17 | #define AUDIO_SERVICE_HPP 18 | 19 | #include 20 | 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | class AudioWorker; 26 | 27 | class AudioService : public QObject 28 | { 29 | Q_OBJECT 30 | 31 | public: 32 | AudioService(QObject * parent = nullptr); 33 | ~AudioService() override; 34 | 35 | void startRecording(QString filePath, quint32 bufferSize); 36 | void stopRecording(); 37 | 38 | private: 39 | void initializeWorker(); 40 | 41 | std::unique_ptr m_audioWorker; 42 | QThread m_audioWorkerThread; 43 | }; 44 | 45 | } // namespace noteahead 46 | 47 | #endif // AUDIO_SERVICE_HPP 48 | -------------------------------------------------------------------------------- /src/unit_tests/pitch_bend_automations_model_test/pitch_bend_automations_model_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef PITCH_BEND_AUTOMATIONS_MODEL_TEST_HPP 17 | #define PITCH_BEND_AUTOMATIONS_MODEL_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class PitchBendAutomationsModelTest : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | private slots: 28 | 29 | void test_addPitchBendAutomations_shouldAddAutomations(); 30 | 31 | void test_requestPitchBendAutomations_shouldFilterAutomations(); 32 | 33 | void test_setData_shouldUpdateAutomationData(); 34 | void test_removeAt_shouldRemoveAutomationData(); 35 | }; 36 | 37 | } // namespace noteahead 38 | 39 | #endif // PITCH_BEND_AUTOMATIONS_MODEL_TEST_HPP 40 | -------------------------------------------------------------------------------- /src/application/ui_logger.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef UI_LOGGER_HPP 17 | #define UI_LOGGER_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class UiLogger : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit UiLogger(QObject * parent = nullptr); 29 | 30 | Q_INVOKABLE void trace(QString tag, QString message) const; 31 | 32 | Q_INVOKABLE void debug(QString tag, QString message) const; 33 | 34 | Q_INVOKABLE void info(QString tag, QString message) const; 35 | 36 | Q_INVOKABLE void warning(QString tag, QString message) const; 37 | 38 | Q_INVOKABLE void error(QString tag, QString message) const; 39 | }; 40 | 41 | } // namespace noteahead 42 | 43 | #endif // UI_LOGGER_HPP 44 | -------------------------------------------------------------------------------- /src/domain/pitch_bend_data.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef PITCH_BEND_DATA_HPP 17 | #define PITCH_BEND_DATA_HPP 18 | 19 | #include "event_data.hpp" 20 | 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | class PitchBendData : public EventData 26 | { 27 | public: 28 | PitchBendData(size_t track, size_t column, uint8_t msb, uint8_t lsb); 29 | PitchBendData(size_t track, size_t column, uint16_t value); 30 | PitchBendData(size_t track, size_t column, double percentage); 31 | 32 | double normalizedValue() const; 33 | 34 | uint8_t msb() const; 35 | uint8_t lsb() const; 36 | 37 | private: 38 | uint8_t m_msb = 0; 39 | uint8_t m_lsb = 0; 40 | }; 41 | } // namespace noteahead 42 | 43 | #endif // PITCH_BEND_DATA_HPP 44 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/TrackSettingsDialog_MidiCcSettings_Custom.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Layouts 5 | import ".." 6 | 7 | GroupBox { 8 | title: qsTr("Custom MIDI CC Settings") 9 | Layout.fillWidth: true 10 | width: parent.width 11 | property var _midiCcSelectors: [] 12 | function initialize(): void { 13 | for (const midiCcSelector of _midiCcSelectors) { 14 | midiCcSelector.initialize(trackSettingsModel.midiCcEnabled(midiCcSelector.index), trackSettingsModel.midiCcController(midiCcSelector.index), trackSettingsModel.midiCcValue(midiCcSelector.index)); 15 | } 16 | } 17 | ColumnLayout { 18 | spacing: 8 19 | width: parent.width 20 | Repeater { 21 | id: midiCcRepeater 22 | model: trackSettingsModel.midiCcSlots 23 | MidiCcSelector {} 24 | onItemAdded: (index, item) => { 25 | item.index = index; 26 | _midiCcSelectors.push(item); 27 | item.settingsChanged.connect(() => { 28 | trackSettingsModel.setMidiCcEnabled(item.index, item.enabled()); 29 | trackSettingsModel.setMidiCcController(item.index, item.controller()); 30 | trackSettingsModel.setMidiCcValue(item.index, item.value()); 31 | trackSettingsModel.applyAll(); 32 | }); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 36 | 40 | 41 | -------------------------------------------------------------------------------- /src/application/instrument_request.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef INSTRUMENT_REQUEST_HPP 17 | #define INSTRUMENT_REQUEST_HPP 18 | 19 | #include "../domain/instrument.hpp" 20 | 21 | namespace noteahead { 22 | 23 | class InstrumentRequest 24 | { 25 | public: 26 | enum class Type 27 | { 28 | None, 29 | ApplyAll, 30 | ApplyPatch, 31 | ApplyMidiCc, 32 | }; 33 | 34 | InstrumentRequest(); 35 | 36 | InstrumentRequest(Type type, const Instrument & instrument); 37 | 38 | Type type() const; 39 | 40 | const Instrument & instrument() const; 41 | 42 | private: 43 | Type m_type = Type::None; 44 | 45 | Instrument m_instrument; 46 | }; 47 | 48 | } // namespace noteahead 49 | 50 | #endif // INSTRUMENT_REQUEST_HPP 51 | -------------------------------------------------------------------------------- /src/unit_tests/midi_cc_automations_model_test/midi_cc_automations_model_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_CC_AUTOMATIONS_MODEL_TEST_HPP 17 | #define MIDI_CC_AUTOMATIONS_MODEL_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class MidiCcAutomationsModelTest : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | private slots: 28 | 29 | void test_addMidiCcAutomations_shouldAddAutomations(); 30 | 31 | void test_requestMidiCcAutomations_shouldFilterAutomations(); 32 | 33 | void test_setData_shouldUpdateAutomationData(); 34 | void test_setData_shouldUpdateModulationData(); 35 | void test_removeAt_shouldRemoveAutomationData(); 36 | }; 37 | 38 | } // namespace noteahead 39 | 40 | #endif // MIDI_CC_AUTOMATIONS_MODEL_TEST_HPP 41 | -------------------------------------------------------------------------------- /src/unit_tests/note_converter_test/note_converter_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef NOTE_CONVERTER_TEST_HPP 17 | #define NOTE_CONVERTER_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class NoteConverterTest : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | private slots: 28 | 29 | void test_midiToString_shouldReturnCorrectString_data(); 30 | void test_midiToString_shouldReturnCorrectString(); 31 | 32 | void test_stringToMidi_shouldReturnCorrectMidiNote_data(); 33 | void test_stringToMidi_shouldReturnCorrectMidiNote(); 34 | 35 | void test_midiToString_shouldThrowOnInvalidInput(); 36 | void test_stringToMidi_shouldThrowOnInvalidInput(); 37 | }; 38 | 39 | } // namespace noteahead 40 | 41 | #endif // NOTE_CONVERTER_TEST_HPP 42 | -------------------------------------------------------------------------------- /src/unit_tests/midi_exporter_test/midi_exporter_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_EXPORTER_TEST_HPP 17 | #define MIDI_EXPORTER_TEST_HPP 18 | 19 | #include 20 | #include 21 | 22 | namespace noteahead { 23 | 24 | class MidiExporterTest : public QObject 25 | { 26 | Q_OBJECT 27 | 28 | private slots: 29 | void test_exportTo_singleNote_shouldExportCorrectly(); 30 | void test_exportTo_multipleNotesAndTracks_shouldExportCorrectly(); 31 | void test_exportTo_timing_shouldBeCorrect(); 32 | void test_exportTo_mutedAndSoloedTracks_shouldExportCorrectly(); 33 | void test_exportTo_rangedExport_shouldExportCorrectRange(); 34 | // void test_readHalla2Mid(); 35 | }; 36 | 37 | } // namespace noteahead 38 | 39 | #endif // MIDI_EXPORTER_TEST_HPP 40 | -------------------------------------------------------------------------------- /src/view/qml/Editor/VelocityScale.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import ".." 4 | 5 | Item { 6 | id: rootItem 7 | readonly property int minValue: 0 8 | readonly property int maxValue: 100 9 | property int value: 100 10 | property string toolTipText 11 | signal clicked 12 | ToolTip.delay: Constants.toolTipDelay 13 | ToolTip.timeout: Constants.toolTipTimeout 14 | ToolTip.visible: hoverHandler.hovered 15 | ToolTip.text: toolTipText 16 | Rectangle { 17 | color: "transparent" 18 | border.color: "white" 19 | border.width: 1 20 | height: parent.height * 0.8 21 | width: 10 22 | anchors.centerIn: parent 23 | clip: true 24 | Rectangle { 25 | id: bar 26 | color: Qt.rgba(value / maxValue, 1 - value / maxValue * (1 - 0.647), 0, 1) 27 | anchors.bottom: parent.bottom 28 | anchors.bottomMargin: parent.border.width 29 | anchors.left: parent.left 30 | anchors.right: parent.right 31 | anchors.leftMargin: parent.border.width 32 | anchors.rightMargin: parent.border.width 33 | height: (value - minValue) * (parent.height - parent.border.width * 2) / (maxValue - minValue) 34 | } 35 | } 36 | HoverHandler { 37 | id: hoverHandler 38 | } 39 | MouseArea { 40 | id: mouseArea 41 | anchors.fill: parent 42 | onClicked: rootItem.clicked() 43 | cursorShape: Qt.PointingHandCursor 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/contrib/Argengine/CHANGELOG: -------------------------------------------------------------------------------- 1 | x.y.z 2 | ===== 3 | 4 | Release date: 5 | 6 | New features: 7 | 8 | Bug fixes: 9 | 10 | Other: 11 | 12 | 1.3.0 13 | ===== 14 | 15 | Release date: 16 | 17 | Sun, 14 Jan 2024 17:02:51 +0200 18 | 19 | New features: 20 | 21 | * Fix GitHub Issue #6: Implement option groups 22 | * Add Argengine::options() for consistency 23 | 24 | Other: 25 | 26 | * Unify type aliases: Use a single type OptionSet for all purposes 27 | * Improve documentation 28 | * Make printHelp() const 29 | 30 | 1.2.0 31 | ===== 32 | 33 | Release date: 34 | 35 | Sun, 15 Oct 2023 22:29:51 +0300 36 | 37 | New features: 38 | 39 | * Fix GitHub Issue #1: Implement conflicting options 40 | 41 | 1.1.1 42 | ===== 43 | 44 | Release date: 45 | 46 | Tue, 06 Sep 2022 18:30:37 -0000 47 | 48 | Bug fixes: 49 | 50 | * Fix typo in the unknown option error message 51 | 52 | Other: 53 | 54 | * Fix GitHub Issue #5: Add documentation of option info texts 55 | * Update documentation of ex1 56 | 57 | 1.1.0 58 | ===== 59 | 60 | New features: 61 | 62 | * Fix GitHub Issue #2: Configurable value names for help 63 | * Add getter for help text 64 | 65 | 1.0.1 66 | ===== 67 | 68 | Bug fixes: 69 | 70 | * Fix include path in tests 71 | 72 | 1.0.0 73 | ===== 74 | 75 | New features: 76 | 77 | * Enable/disable tests on top level 78 | * Build shared and static libs 79 | 80 | Other: 81 | 82 | * Documentation updates 83 | 84 | 0.0.1 85 | ===== 86 | 87 | Initial release. 88 | 89 | New features: 90 | 91 | * Valueless options 92 | * Single-value options 93 | * Help generation 94 | 95 | -------------------------------------------------------------------------------- /src/unit_tests/play_order_test/play_order_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef PLAY_ORDER_TEST_HPP 17 | #define PLAY_ORDER_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class PlayOrderTest : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | private slots: 28 | 29 | void test_initialization_shouldReturnInitialMapping(); 30 | 31 | void test_insertPattern_shouldInsertPattern(); 32 | 33 | void test_getPatterns_shouldReturnCorrectPatterns(); 34 | 35 | void test_length_shouldReturnCorrectLength(); 36 | 37 | void test_removePattern_shouldRemovePattern(); 38 | 39 | void test_setAndGetPattern_shouldReturnCorrectValues(); 40 | 41 | void test_positionToPattern_shouldReturnCorrectValues(); 42 | }; 43 | 44 | } // namespace noteahead 45 | 46 | #endif // PLAY_ORDER_TEST_HPP 47 | -------------------------------------------------------------------------------- /src/domain/note_data_manipulator.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef NOTE_DATA_MANIPULATOR_HPP 17 | #define NOTE_DATA_MANIPULATOR_HPP 18 | 19 | #include "../application/position.hpp" 20 | 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | class Song; 26 | 27 | namespace NoteDataManipulator { 28 | 29 | using SongW = std::weak_ptr; 30 | using ChangedPositions = std::vector; 31 | ChangedPositions interpolateVelocityOnColumn(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue, bool usePercentages); 32 | ChangedPositions interpolateVelocityOnTrack(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue, bool usePercentages); 33 | 34 | } // namespace NoteDataManipulator 35 | } // namespace noteahead 36 | 37 | #endif // NOTE_DATA_MANIPULATOR_HPP 38 | -------------------------------------------------------------------------------- /src/unit_tests/song_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME song_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/position.hpp 7 | ../../application/service/automation_service.cpp 8 | ../../application/service/copy_manager.cpp 9 | ../../application/service/random_service.cpp 10 | ../../application/service/side_chain_service.cpp 11 | ../../common/constants.cpp 12 | ../../common/utils.cpp 13 | ../../domain/automation.cpp 14 | ../../domain/automation_location.cpp 15 | ../../domain/column.cpp 16 | ../../domain/column_settings.cpp 17 | ../../domain/event.cpp 18 | ../../domain/event_data.cpp 19 | ../../domain/instrument.cpp 20 | ../../domain/instrument_settings.cpp 21 | ../../domain/interpolator.cpp 22 | ../../domain/line.cpp 23 | ../../domain/line_event.cpp 24 | ../../domain/midi_address.cpp 25 | ../../domain/midi_cc_automation.cpp 26 | ../../domain/midi_cc_data.cpp 27 | ../../domain/midi_cc_setting.cpp 28 | ../../domain/mixer_unit.cpp 29 | ../../domain/note_data.cpp 30 | ../../domain/pattern.cpp 31 | ../../domain/pitch_bend_automation.cpp 32 | ../../domain/pitch_bend_data.cpp 33 | ../../domain/play_order.cpp 34 | ../../domain/song.cpp 35 | ../../domain/track.cpp 36 | ) 37 | qt_add_executable(${NAME} ${SRC}) 38 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 39 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 40 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 41 | -------------------------------------------------------------------------------- /src/view/qml/Editor/NoteColumn_LineDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | 3 | Rectangle { 4 | id: rootItem 5 | color: model.color 6 | border.color: "#222222" 7 | border.width: 0 8 | opacity: !model.isVirtualRow 9 | Text { 10 | id: textElement 11 | font.pixelSize: parent.height * 0.8 12 | font.family: "monospace" 13 | anchors.centerIn: parent 14 | text: note ? `${model.note} ${model.velocity.padStart(3, "-")}` : "" 15 | color: note && note !== "---" ? "#ffffff" : "#888888" 16 | } 17 | Rectangle { 18 | width: model.lineColumn === 0 ? 3 * (textElement ? textElement.contentWidth : 0) / 7 : (textElement ? textElement.contentWidth : 0) / 7 19 | height: textElement.contentHeight 20 | x: model.lineColumn === 0 ? (textElement ? textElement.x : 0) : (textElement ? textElement.x + (3 + model.lineColumn) * textElement.contentWidth / 7 : 0) 21 | color: "red" 22 | opacity: 0.5 23 | visible: model.isFocused 24 | } 25 | MouseArea { 26 | anchors.fill: parent 27 | acceptedButtons: Qt.LeftButton | Qt.RightButton 28 | onClicked: mouse => { 29 | handleClickOnDelegate(index, rootItem, mouse); 30 | } 31 | onPressed: mouse => { 32 | handlePressOnDelegate(index, rootItem, mouse); 33 | } 34 | onReleased: mouse => { 35 | handleReleaseOnDelegate(index, rootItem, mouse); 36 | } 37 | onPositionChanged: mouse => { 38 | handleMouseMoveOnDelegate(index, rootItem, mouse); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/view/qml/MainMenu/MainMenu_File.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import ".." 5 | import "../Components" 6 | 7 | Menu { 8 | title: qsTr("&File") 9 | Action { 10 | text: qsTr("New...") 11 | shortcut: "Ctrl+N" 12 | onTriggered: applicationService.requestNewProject() 13 | } 14 | MenuSeparator {} 15 | Action { 16 | text: qsTr("Open...") 17 | shortcut: "Ctrl+O" 18 | onTriggered: applicationService.requestOpenProject() 19 | } 20 | Action { 21 | text: qsTr("Recent files") 22 | onTriggered: UiService.requestRecentFilesDialog() 23 | } 24 | MenuSeparator {} 25 | Action { 26 | text: qsTr("Save") 27 | shortcut: "Ctrl+S" 28 | onTriggered: applicationService.requestSaveProject() 29 | enabled: editorService.canBeSaved 30 | } 31 | Action { 32 | text: qsTr("Save as...") 33 | shortcut: "Ctrl+A" 34 | onTriggered: applicationService.requestSaveProjectAs() 35 | } 36 | Action { 37 | text: qsTr("Save as a template...") 38 | onTriggered: applicationService.requestSaveProjectAsTemplate() 39 | } 40 | MenuSeparator {} 41 | Action { 42 | text: qsTr("Export MIDI file") 43 | shortcut: "Ctrl+E" 44 | onTriggered: applicationService.requestMidiExportDialog() 45 | } 46 | MenuSeparator {} 47 | Action { 48 | text: qsTr("Quit") 49 | shortcut: "Ctrl+Q" 50 | onTriggered: UiService.requestQuit() 51 | } 52 | delegate: MenuItemDelegate {} 53 | } 54 | -------------------------------------------------------------------------------- /src/unit_tests/midi_cc_selection_model_test/midi_cc_selection_model_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_CC_SELECTION_MODEL_TEST_HPP 17 | #define MIDI_CC_SELECTION_MODEL_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class MidiCcSelectionModel; 24 | 25 | class MidiCcSelectionModelTest : public QObject 26 | { 27 | Q_OBJECT 28 | 29 | private slots: 30 | void initTestCase(); 31 | void cleanupTestCase(); 32 | 33 | void test_controller_shouldBeSetAndRetrieved(); 34 | void test_value_shouldBeSetAndRetrieved(); 35 | void test_enabled_shouldBeSetAndRetrieved(); 36 | void test_toString_shouldReturnNonEmptyString(); 37 | void test_settings_shouldRoundTripCorrectly(); 38 | 39 | private: 40 | MidiCcSelectionModel * model = nullptr; 41 | }; 42 | 43 | } // namespace noteahead 44 | 45 | #endif // MIDI_CC_SELECTION_MODEL_TEST_HPP 46 | -------------------------------------------------------------------------------- /src/domain/interpolator.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "interpolator.hpp" 17 | 18 | namespace noteahead { 19 | 20 | Interpolator::Interpolator(size_t startLine, size_t endLine, double startValue, double endValue) 21 | : m_startLine { startLine } 22 | , m_endLine { endLine } 23 | , m_startValue { startValue } 24 | , m_endValue { endValue } 25 | { 26 | } 27 | 28 | double Interpolator::getValue(size_t line) const 29 | { 30 | if (line <= m_startLine) { 31 | return m_startValue; 32 | } 33 | 34 | if (line >= m_endLine) { 35 | return m_endValue; 36 | } 37 | 38 | if (m_startLine == m_endLine) { 39 | return m_startValue; 40 | } 41 | 42 | const double t = static_cast(line - m_startLine) / static_cast(m_endLine - m_startLine); 43 | const double d = m_endValue - m_startValue; 44 | return m_startValue + t * d; 45 | } 46 | 47 | } // namespace noteahead 48 | -------------------------------------------------------------------------------- /src/unit_tests/side_chain_service_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME side_chain_service_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/service/automation_service.cpp 7 | ../../application/service/copy_manager.cpp 8 | ../../application/service/random_service.cpp 9 | ../../application/service/side_chain_service.cpp 10 | ../../common/constants.cpp 11 | ../../common/utils.cpp 12 | ../../domain/automation.cpp 13 | ../../domain/automation_location.cpp 14 | ../../domain/column.cpp 15 | ../../domain/column_settings.cpp 16 | ../../domain/event.cpp 17 | ../../domain/event_data.cpp 18 | ../../domain/instrument.cpp 19 | ../../domain/instrument_settings.cpp 20 | ../../domain/interpolator.cpp 21 | ../../domain/line.cpp 22 | ../../domain/line_event.cpp 23 | ../../domain/midi_address.cpp 24 | ../../domain/midi_cc_automation.cpp 25 | ../../domain/midi_cc_data.cpp 26 | ../../domain/midi_cc_setting.cpp 27 | ../../domain/midi_note_data.cpp 28 | ../../domain/mixer_unit.cpp 29 | ../../domain/note_data.cpp 30 | ../../domain/note_data_manipulator.cpp 31 | ../../domain/pattern.cpp 32 | ../../domain/pitch_bend_automation.cpp 33 | ../../domain/pitch_bend_data.cpp 34 | ../../domain/play_order.cpp 35 | ../../domain/song.cpp 36 | ../../domain/track.cpp 37 | ) 38 | qt_add_executable(${NAME} ${SRC}) 39 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 40 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 41 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) -------------------------------------------------------------------------------- /src/view/qml/Dialogs/ColumnSettingsDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | import QtQuick.Layouts 6 | import ".." 7 | import "../Components" 8 | 9 | Dialog { 10 | id: rootItem 11 | title: "" + qsTr("Column settings for track %1, column %2").arg(columnSettingsModel.trackIndex + 1).arg(columnSettingsModel.columnIndex + 1) + "" 12 | modal: true 13 | function initialize() { 14 | midiEffects.initialize(); 15 | } 16 | function saveSettings() { 17 | columnSettingsModel.save(); 18 | } 19 | function setColumn(trackIndex, columnIndex) { 20 | columnSettingsModel.trackIndex = trackIndex; 21 | columnSettingsModel.columnIndex = columnIndex; 22 | columnSettingsModel.requestData(); 23 | } 24 | footer: DialogButtonBox { 25 | Button { 26 | text: qsTr("Ok") 27 | DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole 28 | onClicked: { 29 | saveSettings(); 30 | rootItem.accepted(); 31 | } 32 | } 33 | Button { 34 | text: qsTr("Cancel") 35 | DialogButtonBox.buttonRole: DialogButtonBox.RejectRole 36 | onClicked: rootItem.rejected() 37 | } 38 | } 39 | Column { 40 | anchors.fill: parent 41 | spacing: 10 42 | ColumnSettingsDialog_MidiEffects { 43 | id: midiEffects 44 | Layout.fillWidth: true 45 | } 46 | } 47 | 48 | Component.onCompleted: { 49 | columnSettingsModel.dataReceived.connect(initialize); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/application/service/recent_files_manager.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef RECENT_FILES_MANAGER_HPP 17 | #define RECENT_FILES_MANAGER_HPP 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | namespace noteahead { 26 | 27 | class RecentFilesManager : public QObject 28 | { 29 | Q_OBJECT 30 | 31 | public: 32 | RecentFilesManager(); 33 | 34 | std::optional recentFile() const; 35 | QStringList recentFiles() const; 36 | bool hasRecentFiles() const; 37 | 38 | QString selectedFile() const; 39 | 40 | signals: 41 | void recentFilesChanged(const QStringList & recentFiles); 42 | 43 | public slots: 44 | void addRecentFile(QString filePath); 45 | void setSelectedFile(QString filePath); 46 | 47 | private: 48 | QStringList m_recentFiles; 49 | QString m_selectedFile; 50 | }; 51 | 52 | } // namespace noteahead 53 | 54 | #endif // RECENT_FILES_MANAGER_HPP 55 | -------------------------------------------------------------------------------- /src/application/models/recent_files_model.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef RECENT_FILES_MODEL_HPP 17 | #define RECENT_FILES_MODEL_HPP 18 | 19 | #include 20 | #include 21 | 22 | namespace noteahead { 23 | 24 | class RecentFilesModel : public QAbstractListModel 25 | { 26 | Q_OBJECT 27 | 28 | public: 29 | enum class Role 30 | { 31 | FilePath = Qt::UserRole + 1, 32 | Exists 33 | }; 34 | 35 | explicit RecentFilesModel(QObject * parent = nullptr); 36 | 37 | void setRecentFiles(const QStringList & list); 38 | 39 | Q_INVOKABLE int rowCount(const QModelIndex & parent = QModelIndex()) const override; 40 | Q_INVOKABLE QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; 41 | Q_INVOKABLE QHash roleNames() const override; 42 | 43 | private: 44 | QStringList m_recentFiles; 45 | }; 46 | 47 | } // namespace noteahead 48 | 49 | #endif // RECENT_FILES_MODEL_HPP 50 | -------------------------------------------------------------------------------- /src/domain/automation.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef AUTOMATION_HPP 17 | #define AUTOMATION_HPP 18 | 19 | #include "automation_location.hpp" 20 | 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | //! Base class for automations. 26 | class Automation 27 | { 28 | public: 29 | Automation(); 30 | Automation(size_t id, AutomationLocation location, QString comment, bool enabled); 31 | 32 | size_t id() const; 33 | void setId(size_t id); 34 | 35 | QString comment() const; 36 | void setComment(QString comment); 37 | 38 | bool enabled() const; 39 | void setEnabled(bool enabled); 40 | 41 | const AutomationLocation & location() const; 42 | void setLocation(const AutomationLocation & location); 43 | 44 | private: 45 | size_t m_id = 0; 46 | 47 | QString m_comment; 48 | 49 | bool m_enabled = true; 50 | 51 | AutomationLocation m_location; 52 | }; 53 | 54 | } // namespace noteahead 55 | 56 | #endif // AUTOMATION_HPP 57 | -------------------------------------------------------------------------------- /src/view/qml/Editor/MainContextMenu_Line.qml: -------------------------------------------------------------------------------- 1 | import QtQuick.Controls 2.15 2 | import QtQuick.Controls.Universal 2.15 3 | import ".." 4 | import "../Components" 5 | 6 | Menu { 7 | title: qsTr("Line") 8 | width: rootItem.width 9 | Action { 10 | text: qsTr("Insert an event") 11 | shortcut: "Alt+E" 12 | enabled: !UiService.isPlaying() 13 | onTriggered: UiService.requestEventSelectionDialog() 14 | } 15 | Action { 16 | text: qsTr("Remove current event") 17 | shortcut: "Alt+Shift+E" 18 | enabled: !UiService.isPlaying() 19 | onTriggered: editorService.requestEventRemoval() 20 | } 21 | MenuSeparator {} 22 | Action { 23 | text: qsTr("Set delay") 24 | enabled: !UiService.isPlaying() 25 | onTriggered: UiService.requestLineDelayDialog() 26 | } 27 | MenuSeparator {} 28 | Action { 29 | text: qsTr("Add MIDI CC automation") 30 | enabled: !UiService.isPlaying() 31 | onTriggered: UiService.requestLineAddMidiCcAutomationDialog() 32 | } 33 | Action { 34 | text: qsTr("Edit MIDI CC automations") 35 | enabled: !UiService.isPlaying() 36 | onTriggered: UiService.requestEditMidiCcAutomationsDialogByLine() 37 | } 38 | MenuSeparator {} 39 | Action { 40 | text: qsTr("Add Pitch Bend automation") 41 | enabled: !UiService.isPlaying() 42 | onTriggered: UiService.requestLineAddPitchBendAutomationDialog() 43 | } 44 | Action { 45 | text: qsTr("Edit Pitch Bend automations") 46 | enabled: !UiService.isPlaying() 47 | onTriggered: UiService.requestEditPitchBendAutomationsDialogByLine() 48 | } 49 | delegate: MenuItemDelegate {} 50 | } 51 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/GainConverterDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Layouts 5 | 6 | Dialog { 7 | id: gainConverterDialog 8 | title: qsTr("Gain Converter (dB => linear)") 9 | modal: true 10 | clip: true 11 | standardButtons: Dialog.Ok 12 | property real dbValue: 0.0 13 | property real linearValue: 1.0 14 | property bool updating: false 15 | ColumnLayout { 16 | anchors.fill: parent 17 | spacing: 10 18 | clip: true 19 | RowLayout { 20 | Layout.fillWidth: true 21 | SpinBox { 22 | id: dbSpinBox 23 | from: -60 24 | to: 24 25 | value: dbValue 26 | stepSize: 1 27 | editable: true 28 | onValueChanged: { 29 | gainConverterDialog.dbValue = value; 30 | gainConverterDialog.calculateFromDb(); 31 | } 32 | } 33 | Label { 34 | text: qsTr("dB") 35 | verticalAlignment: Label.AlignVCenter 36 | padding: 4 37 | } 38 | Label { 39 | id: linearFromDbLabel 40 | Layout.fillWidth: true 41 | text: `= ${gainConverterDialog.linearValue.toFixed(3)}` 42 | font.bold: true 43 | font.pixelSize: dbSpinBox.height 44 | } 45 | } 46 | } 47 | function calculateFromDb(): void { 48 | linearValue = Math.pow(10, dbValue / 20); 49 | linearFromDbLabel.text = `= ${linearValue.toFixed(3)}`; 50 | } 51 | Component.onCompleted: calculateFromDb() 52 | } 53 | -------------------------------------------------------------------------------- /src/domain/midi_cc_setting.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_CC_SETTING_HPP 17 | #define MIDI_CC_SETTING_HPP 18 | 19 | #include 20 | #include 21 | 22 | class QXmlStreamWriter; 23 | class QXmlStreamReader; 24 | 25 | namespace noteahead { 26 | 27 | class MidiCcSetting 28 | { 29 | public: 30 | MidiCcSetting(bool enabled, uint8_t controller, uint8_t value); 31 | MidiCcSetting(); 32 | 33 | bool enabled() const; 34 | void setEnabled(bool enabled); 35 | 36 | uint8_t controller() const; 37 | void setController(uint8_t controller); 38 | 39 | uint8_t value() const; 40 | void setValue(uint8_t value); 41 | 42 | void serializeToXml(QXmlStreamWriter & writer) const; 43 | static std::unique_ptr deserializeFromXml(QXmlStreamReader & reader); 44 | 45 | QString toString() const; 46 | 47 | private: 48 | bool m_enabled = false; 49 | uint8_t m_controller = 0; 50 | uint8_t m_value = 0; 51 | }; 52 | 53 | } // namespace noteahead 54 | 55 | #endif // MIDI_CC_SETTING_HPP 56 | -------------------------------------------------------------------------------- /src/domain/pitch_bend_data.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "pitch_bend_data.hpp" 17 | 18 | namespace noteahead { 19 | 20 | PitchBendData::PitchBendData(size_t track, size_t column, uint8_t msb, uint8_t lsb) 21 | : EventData { track, column } 22 | , m_msb { msb } 23 | , m_lsb { lsb } 24 | { 25 | } 26 | 27 | PitchBendData::PitchBendData(size_t track, size_t column, uint16_t value) 28 | : PitchBendData { track, column, static_cast((value >> 7) & 0x7F), static_cast(value & 0x7F) } 29 | { 30 | } 31 | 32 | PitchBendData::PitchBendData(size_t track, size_t column, double percentage) 33 | : PitchBendData { track, column, static_cast((percentage + 100.0) * (16383.0 / 200.0)) } 34 | { 35 | } 36 | 37 | double PitchBendData::normalizedValue() const 38 | { 39 | return static_cast((m_msb << 7) | m_lsb) / 16383.0; 40 | } 41 | 42 | uint8_t PitchBendData::msb() const 43 | { 44 | return m_msb; 45 | } 46 | 47 | uint8_t PitchBendData::lsb() const 48 | { 49 | return m_lsb; 50 | } 51 | 52 | } // namespace noteahead 53 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/AboutDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | import QtQuick.Layouts 6 | 7 | Dialog { 8 | id: rootItem 9 | title: `${qsTr("About ")} ${applicationService.applicationName()} ${qsTr("MIDI tracker v")}${applicationService.applicationVersion()}` 10 | modal: true 11 | standardButtons: DialogButtonBox.Ok 12 | RowLayout { 13 | spacing: 10 14 | Image { 15 | id: icon 16 | height: 256 17 | width: 256 18 | sourceSize: Qt.size(height, width) 19 | fillMode: Image.PreserveAspectFit 20 | source: "../Graphics/icon.svg" 21 | } 22 | ColumnLayout { 23 | Label { 24 | text: `${qsTr("Licensed under")} ${applicationService.license()}` 25 | } 26 | Label { 27 | text: " " 28 | } 29 | Label { 30 | text: `${applicationService.copyright()}` 31 | } 32 | Label { 33 | text: " " 34 | } 35 | Label { 36 | id: link_Text 37 | text: `${qsTr("Project website:")} ${applicationService.webSiteUrl()}` 38 | onLinkActivated: link => Qt.openUrlExternally(link) 39 | MouseArea { 40 | id: mouseArea 41 | anchors.fill: parent 42 | acceptedButtons: Qt.NoButton // Don't eat the mouse clicks 43 | cursorShape: Qt.PointingHandCursor 44 | } 45 | } 46 | } 47 | } 48 | Component.onCompleted: { 49 | visible = false; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/domain/line_event.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef LINE_EVENT_HPP 17 | #define LINE_EVENT_HPP 18 | 19 | #include 20 | 21 | class QXmlStreamReader; 22 | class QXmlStreamWriter; 23 | 24 | namespace noteahead { 25 | 26 | class InstrumentSettings; 27 | 28 | class LineEvent 29 | { 30 | public: 31 | LineEvent(size_t trackIndex, size_t columnIndex); 32 | 33 | using InstrumentSettingsS = std::shared_ptr; 34 | InstrumentSettingsS instrumentSettings() const; 35 | void setInstrumentSettings(InstrumentSettingsS instrumentSettings); 36 | 37 | bool hasData() const; 38 | 39 | void serializeToXml(QXmlStreamWriter & writer) const; 40 | using LineEventU = std::shared_ptr; 41 | static LineEventU deserializeFromXml(QXmlStreamReader & reader, size_t trackIndex, size_t columnIndex); 42 | 43 | private: 44 | InstrumentSettingsS m_instrumentSettings; 45 | 46 | size_t m_trackIndex = 0; 47 | 48 | size_t m_columnIndex = 0; 49 | }; 50 | 51 | } // namespace noteahead 52 | 53 | #endif // LINE_EVENT_HPP 54 | -------------------------------------------------------------------------------- /src/domain/play_order.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef PLAY_ORDER_HPP 17 | #define PLAY_ORDER_HPP 18 | 19 | #include 20 | #include 21 | 22 | class QXmlStreamWriter; 23 | 24 | namespace noteahead { 25 | 26 | class PlayOrder 27 | { 28 | public: 29 | PlayOrder(); 30 | 31 | size_t length() const; 32 | 33 | using PatternList = std::vector; 34 | PatternList getPatterns(size_t songLength, size_t startPosition = 0) const; 35 | 36 | void insertPattern(size_t position, size_t pattern); 37 | void removePattern(size_t position); 38 | 39 | void setPatternAtPosition(size_t position, size_t pattern); 40 | size_t positionToPattern(size_t position) const; 41 | 42 | void serializeToXml(QXmlStreamWriter & writer) const; 43 | void serializeToXml(QXmlStreamWriter & writer, size_t lastPosition) const; 44 | 45 | private: 46 | void serializePosition(QXmlStreamWriter & writer, size_t position) const; 47 | 48 | std::vector m_positionToPattern; 49 | }; 50 | 51 | } // namespace noteahead 52 | 53 | #endif // PLAY_ORDER_HPP 54 | -------------------------------------------------------------------------------- /src/unit_tests/column_settings_model_test/column_settings_model_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef COLUMN_SETTINGS_MODEL_TEST_HPP 17 | #define COLUMN_SETTINGS_MODEL_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class ColumnSettingsModelTest : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | private slots: 28 | void test_initialState_shouldHaveExpectedDefaults(); 29 | void test_setTrackIndex_shouldUpdateAndEmitSignal(); 30 | void test_setColumnIndex_shouldUpdateAndEmitSignal(); 31 | void test_setChordNote1Offset_shouldUpdateAndEmitSignal(); 32 | void test_setChordNote1Velocity_shouldUpdateAndEmitSignal(); 33 | void test_setChordNote2Offset_shouldUpdateAndEmitSignal(); 34 | void test_setChordNote2Velocity_shouldUpdateAndEmitSignal(); 35 | void test_setChordNote3Offset_shouldUpdateAndEmitSignal(); 36 | void test_setChordNote3Velocity_shouldUpdateAndEmitSignal(); 37 | void test_save_shouldEmitSaveRequestedWithCorrectData(); 38 | }; 39 | 40 | } // namespace noteahead 41 | 42 | #endif // COLUMN_SETTINGS_MODEL_TEST_HPP 43 | -------------------------------------------------------------------------------- /src/domain/side_chain_settings.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef SIDE_CHAIN_SETTINGS_HPP 17 | #define SIDE_CHAIN_SETTINGS_HPP 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | struct SideChainSettings 26 | { 27 | //! Enable/disable the side-chain functionality. 28 | bool enabled { false }; 29 | 30 | //! Side-chain source track. 31 | uint8_t sourceTrackIndex { 0 }; 32 | //! Side-chain source column. 33 | uint8_t sourceColumnIndex { 0 }; 34 | 35 | //! Time before the source event. 36 | std::chrono::milliseconds lookahead { 0 }; 37 | //! Time after the source event. 38 | std::chrono::milliseconds release { 0 }; 39 | 40 | //! MIDI CC controller target definitions. 41 | struct Target 42 | { 43 | bool enabled { false }; 44 | uint8_t controller { 0 }; 45 | uint8_t targetValue { 0 }; 46 | uint8_t releaseValue { 0 }; 47 | }; 48 | std::vector targets; 49 | }; 50 | 51 | } // namespace noteahead 52 | 53 | #endif // SIDE_CHAIN_SETTINGS_HPP 54 | -------------------------------------------------------------------------------- /src/unit_tests/selection_service_test/selection_service_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef SELECTION_SERVICE_TEST_HPP 17 | #define SELECTION_SERVICE_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class SelectionServiceTest : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | private slots: 28 | void test_selectedPositions_shouldReturnEmptyIfInvalid(); 29 | void test_selectedPositions_shouldReturnCorrectRange(); 30 | void test_selectedPositions_reversed_shouldReturnCorrectRange(); 31 | 32 | void test_isValidSelection_shouldReturnFalseForIncompleteSelection(); 33 | void test_isValidSelection_shouldReturnTrueForValidSelection(); 34 | 35 | void test_isSelected_shouldReturnTrueForSelectedPosition(); 36 | void test_isSelected_shouldReturnFalseForNonSelectedPosition(); 37 | 38 | void test_requestSelectionStart_shouldSetStartPosition(); 39 | void test_requestSelectionEnd_shouldSetEndPosition(); 40 | 41 | void test_clear_shouldResetSelection(); 42 | }; 43 | 44 | } // namespace noteahead 45 | 46 | #endif // SELECTION_SERVICE_TEST_HPP 47 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/AddMidiCcAutomationDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | import QtQuick.Layouts 6 | 7 | Dialog { 8 | id: rootItem 9 | modal: true 10 | standardButtons: Dialog.Ok | Dialog.Cancel 11 | function setTitle(text: var): void { 12 | title = `${text}`; 13 | } 14 | function controller(): var { 15 | return model.controller(); 16 | } 17 | function startValue(): int { 18 | return model.startValue(); 19 | } 20 | function setStartValue(value): void { 21 | model.setStartValue(value); 22 | } 23 | function endValue(): int { 24 | return model.endValue(); 25 | } 26 | function setEndValue(value: int): void { 27 | model.setEndValue(value); 28 | } 29 | function startLine(): int { 30 | return model.startLine(); 31 | } 32 | function setStartLine(value: int): void { 33 | model.setStartLine(value); 34 | } 35 | function endLine(): int { 36 | return model.endLine(); 37 | } 38 | function setEndLine(value: int): void { 39 | model.setEndLine(value); 40 | } 41 | 42 | function cycles(): int { 43 | return model.cycles(); 44 | } 45 | function amplitude(): int { 46 | return model.amplitude(); 47 | } 48 | function inverted(): bool { 49 | return model.inverted(); 50 | } 51 | function resetModulations(): void { 52 | model.resetModulations(); 53 | } 54 | 55 | function comment(): string { 56 | return model.comment(); 57 | } 58 | function setComment(comment: string): void { 59 | model.setComment(comment); 60 | } 61 | 62 | contentItem: MidiCcAutomationModel { 63 | id: model 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/application/ui_logger.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "ui_logger.hpp" 17 | 18 | #include "../contrib/SimpleLogger/src/simple_logger.hpp" 19 | 20 | namespace noteahead { 21 | 22 | static const auto TAG = "Ui: "; 23 | 24 | UiLogger::UiLogger(QObject * parent) 25 | : QObject { parent } 26 | { 27 | } 28 | 29 | void UiLogger::trace(QString tag, QString message) const 30 | { 31 | juzzlin::L(TAG + tag.toStdString()).trace() << message.toStdString(); 32 | } 33 | 34 | void UiLogger::debug(QString tag, QString message) const 35 | { 36 | juzzlin::L(TAG + tag.toStdString()).debug() << message.toStdString(); 37 | } 38 | 39 | void UiLogger::info(QString tag, QString message) const 40 | { 41 | juzzlin::L(TAG + tag.toStdString()).info() << message.toStdString(); 42 | } 43 | 44 | void UiLogger::warning(QString tag, QString message) const 45 | { 46 | juzzlin::L(TAG + tag.toStdString()).warning() << message.toStdString(); 47 | } 48 | 49 | void UiLogger::error(QString tag, QString message) const 50 | { 51 | juzzlin::L(TAG + tag.toStdString()).error() << message.toStdString(); 52 | } 53 | 54 | } // namespace noteahead 55 | -------------------------------------------------------------------------------- /src/unit_tests/midi_exporter_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | set(NAME midi_exporter_test) 4 | 5 | set(SRC 6 | ${NAME}.cpp 7 | ../../application/service/automation_service.cpp 8 | ../../application/service/copy_manager.cpp 9 | ../../application/service/mixer_service.cpp 10 | ../../application/service/random_service.cpp 11 | ../../application/service/side_chain_service.cpp 12 | ../../common/constants.cpp 13 | ../../common/utils.cpp 14 | ../../domain/automation.cpp 15 | ../../domain/automation_location.cpp 16 | ../../domain/column.cpp 17 | ../../domain/column_settings.cpp 18 | ../../domain/event.cpp 19 | ../../domain/event_data.cpp 20 | ../../domain/instrument.cpp 21 | ../../domain/instrument_settings.cpp 22 | ../../domain/interpolator.cpp 23 | ../../domain/line.cpp 24 | ../../domain/line_event.cpp 25 | ../../domain/midi_address.cpp 26 | ../../domain/midi_cc_automation.cpp 27 | ../../domain/midi_cc_data.cpp 28 | ../../domain/midi_cc_setting.cpp 29 | ../../domain/mixer_unit.cpp 30 | ../../domain/note_data.cpp 31 | ../../domain/note_data_manipulator.cpp 32 | ../../domain/pattern.cpp 33 | ../../domain/pitch_bend_automation.cpp 34 | ../../domain/pitch_bend_data.cpp 35 | ../../domain/play_order.cpp 36 | ../../domain/song.cpp 37 | ../../domain/track.cpp 38 | ../../infra/midi/export/midi_exporter.cpp 39 | ) 40 | 41 | qt_add_executable(${NAME} ${SRC}) 42 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 43 | target_include_directories(${NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../..) 44 | target_include_directories(${NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../contrib/SimpleLogger/src) 45 | target_link_libraries(${NAME} PRIVATE Qt6::Test SimpleLogger) 46 | add_test(NAME ${NAME} COMMAND ${NAME}) 47 | -------------------------------------------------------------------------------- /src/domain/midi_address.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_ADDRESS_HPP 17 | #define MIDI_ADDRESS_HPP 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | class QXmlStreamReader; 25 | class QXmlStreamWriter; 26 | 27 | namespace noteahead { 28 | 29 | class MidiAddress 30 | { 31 | public: 32 | explicit MidiAddress(QString portName); 33 | MidiAddress(QString portName, uint8_t channel); 34 | MidiAddress(QString portName, uint8_t channel, uint8_t group); 35 | 36 | void serializeToXml(QXmlStreamWriter & writer) const; 37 | using MidiAddressU = std::unique_ptr; 38 | static MidiAddressU deserializeFromXml(QXmlStreamReader & reader); 39 | 40 | QString portName() const; 41 | void setPort(QString portName); 42 | 43 | uint8_t channel() const; 44 | void setChannel(uint8_t channel); 45 | 46 | uint8_t group() const; 47 | void setGroup(uint8_t group); 48 | 49 | private: 50 | QString m_portName; 51 | uint8_t m_channel = 0; 52 | uint8_t m_group = 0; // MIDI 2.0 addition 53 | }; 54 | 55 | } // namespace noteahead 56 | 57 | #endif // MIDI_ADDRESS_HPP 58 | -------------------------------------------------------------------------------- /src/domain/instrument.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef INSTRUMENT_HPP 17 | #define INSTRUMENT_HPP 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #include "instrument_settings.hpp" 25 | #include "midi_address.hpp" 26 | 27 | class QXmlStreamReader; 28 | class QXmlStreamWriter; 29 | 30 | namespace noteahead { 31 | 32 | class Instrument 33 | { 34 | public: 35 | explicit Instrument(const QString & portName); 36 | explicit Instrument(const MidiAddress & midiAddress); 37 | 38 | const MidiAddress & midiAddress() const; 39 | void setMidiAddress(const MidiAddress & midiAddress); 40 | 41 | const InstrumentSettings & settings() const; 42 | void setSettings(const InstrumentSettings & settings); 43 | 44 | void serializeToXml(QXmlStreamWriter & writer) const; 45 | using InstrumentU = std::unique_ptr; 46 | static InstrumentU deserializeFromXml(QXmlStreamReader & reader); 47 | 48 | QString toString() const; 49 | 50 | private: 51 | MidiAddress m_midiAddress; 52 | 53 | InstrumentSettings m_settings; 54 | }; 55 | 56 | } // namespace noteahead 57 | 58 | #endif // INSTRUMENT_HPP 59 | -------------------------------------------------------------------------------- /src/view/qml/Graphics/replay.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 36 | 38 | 43 | 48 | 53 | 54 | 57 | 60 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/unit_tests/editor_service_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | set(NAME editor_service_test) 3 | set(SRC 4 | ${NAME}.cpp 5 | ${NAME}.hpp 6 | ../../application/instrument_request.cpp 7 | ../../application/note_converter.cpp 8 | ../../application/position.hpp 9 | ../../application/service/automation_service.cpp 10 | ../../application/service/copy_manager.cpp 11 | ../../application/service/editor_service.cpp 12 | ../../application/service/mixer_service.cpp 13 | ../../application/service/random_service.cpp 14 | ../../application/service/selection_service.cpp 15 | ../../application/service/side_chain_service.cpp 16 | ../../common/constants.cpp 17 | ../../common/utils.cpp 18 | ../../domain/automation.cpp 19 | ../../domain/automation_location.cpp 20 | ../../domain/column.cpp 21 | ../../domain/column_settings.cpp 22 | ../../domain/event.cpp 23 | ../../domain/event_data.cpp 24 | ../../domain/instrument.cpp 25 | ../../domain/instrument_settings.cpp 26 | ../../domain/interpolator.cpp 27 | ../../domain/line.cpp 28 | ../../domain/line_event.cpp 29 | ../../domain/midi_address.cpp 30 | ../../domain/midi_cc_automation.cpp 31 | ../../domain/midi_cc_data.cpp 32 | ../../domain/midi_cc_setting.cpp 33 | ../../domain/mixer_unit.cpp 34 | ../../domain/note_data.cpp 35 | ../../domain/note_data_manipulator.cpp 36 | ../../domain/pattern.cpp 37 | ../../domain/pitch_bend_automation.cpp 38 | ../../domain/pitch_bend_data.cpp 39 | ../../domain/play_order.cpp 40 | ../../domain/song.cpp 41 | ../../domain/track.cpp 42 | ) 43 | qt_add_executable(${NAME} ${SRC}) 44 | set_target_properties(${NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNIT_TEST_BASE_DIR}) 45 | add_test(${NAME} ${UNIT_TEST_BASE_DIR}/${NAME}) 46 | target_link_libraries(${NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Test SimpleLogger) 47 | -------------------------------------------------------------------------------- /src/domain/automation_location.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef AUTOMATION_LOCATION_HPP 17 | #define AUTOMATION_LOCATION_HPP 18 | 19 | #include 20 | #include 21 | 22 | class QXmlStreamReader; 23 | class QXmlStreamWriter; 24 | 25 | namespace noteahead { 26 | 27 | class AutomationLocation 28 | { 29 | public: 30 | AutomationLocation(); 31 | AutomationLocation(size_t pattern, size_t track, size_t column); 32 | 33 | bool operator==(const AutomationLocation & other) const; 34 | bool operator!=(const AutomationLocation & other) const; 35 | 36 | void serializeToXml(QXmlStreamWriter & writer) const; 37 | using AutomationLocationU = std::unique_ptr; 38 | static AutomationLocationU deserializeFromXml(QXmlStreamReader & reader); 39 | 40 | size_t pattern() const; 41 | void setPattern(size_t pattern); 42 | 43 | size_t track() const; 44 | void setTrack(size_t track); 45 | 46 | size_t column() const; 47 | void setColumn(size_t column); 48 | 49 | private: 50 | size_t m_pattern = 0; 51 | size_t m_track = 0; 52 | size_t m_column = 0; 53 | }; 54 | 55 | } // namespace noteahead 56 | 57 | #endif // AUTOMATION_LOCATION_HPP 58 | -------------------------------------------------------------------------------- /src/infra/settings.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2020 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef SETTINGS_HPP 17 | #define SETTINGS_HPP 18 | 19 | #include 20 | 21 | #include 22 | 23 | namespace noteahead::Settings { 24 | 25 | int autoNoteOffOffset(int defaultAutoNoteOffOffset); 26 | void setAutoNoteOffOffset(int autoNoteOffOffset); 27 | 28 | QString controllerPort(QString defaultControllerPort); 29 | void setControllerPort(QString controllerPort); 30 | 31 | QSize windowSize(QSize defaultSize); 32 | void setWindowSize(QSize size); 33 | 34 | QStringList recentFiles(); 35 | void setRecentFiles(const QStringList & fileList); 36 | 37 | int step(int defaultStep); 38 | void setStep(int step); 39 | 40 | int velocity(int defaultVelocity); 41 | void setVelocity(int velocity); 42 | 43 | int visibleLines(int defaultVisibleLines); 44 | void setVisibleLines(int visibleLines); 45 | 46 | int trackHeaderFontSize(int defaultTrackHeaderFontSize); 47 | void setTrackHeaderFontSize(int trackHeaderFontSize); 48 | 49 | bool recordingEnabled(); 50 | void setRecordingEnabled(bool enabled); 51 | 52 | int audioBufferSize(); 53 | void setAudioBufferSize(int audioBufferSize); 54 | 55 | } // namespace noteahead::Settings 56 | 57 | #endif // SETTINGS_HPP 58 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/SettingsDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Dialogs 5 | import QtQuick.Layouts 6 | import ".." 7 | 8 | Dialog { 9 | id: rootItem 10 | title: "" + qsTr("Settings") + "" 11 | modal: true 12 | footer: DialogButtonBox { 13 | Button { 14 | text: qsTr("Ok") 15 | DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole 16 | onClicked: { 17 | rootItem.accepted(); 18 | } 19 | ToolTip.delay: Constants.toolTipDelay 20 | ToolTip.timeout: Constants.toolTipTimeout 21 | ToolTip.visible: hovered 22 | ToolTip.text: qsTr("Save current settings") 23 | } 24 | } 25 | Column { 26 | anchors.fill: parent 27 | spacing: 10 28 | StackLayout { 29 | id: mainLayout 30 | height: parent.height - tabBar.height 31 | width: parent.width 32 | currentIndex: tabBar.currentIndex 33 | SettingsDialog_GeneralSettings { 34 | height: parent.height 35 | width: parent.width 36 | } 37 | SettingsDialog_MidiSettings { 38 | height: parent.height 39 | width: parent.width 40 | } 41 | SettingsDialog_AudioSettings { 42 | height: parent.height 43 | width: parent.width 44 | } 45 | } 46 | TabBar { 47 | id: tabBar 48 | width: parent.width 49 | TabButton { 50 | text: qsTr("General") 51 | } 52 | TabButton { 53 | text: qsTr("MIDI") 54 | } 55 | TabButton { 56 | text: qsTr("Audio") 57 | } 58 | } 59 | } 60 | Component.onCompleted: { 61 | visible = false; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/domain/automation.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "automation.hpp" 17 | 18 | #include "../common/constants.hpp" 19 | 20 | #include 21 | 22 | namespace noteahead { 23 | 24 | Automation::Automation() = default; 25 | 26 | Automation::Automation(size_t id, AutomationLocation location, QString comment, bool enabled) 27 | : m_id { id } 28 | , m_comment { comment } 29 | , m_enabled { enabled } 30 | , m_location { location } 31 | { 32 | } 33 | 34 | size_t Automation::id() const 35 | { 36 | return m_id; 37 | } 38 | 39 | void Automation::setId(size_t id) 40 | { 41 | m_id = id; 42 | } 43 | 44 | QString Automation::comment() const 45 | { 46 | return m_comment; 47 | } 48 | 49 | void Automation::setComment(QString comment) 50 | { 51 | m_comment = comment; 52 | } 53 | 54 | bool Automation::enabled() const 55 | { 56 | return m_enabled; 57 | } 58 | 59 | void Automation::setEnabled(bool enabled) 60 | { 61 | m_enabled = enabled; 62 | } 63 | 64 | const AutomationLocation & Automation::location() const 65 | { 66 | return m_location; 67 | } 68 | 69 | void Automation::setLocation(const AutomationLocation & location) 70 | { 71 | m_location = location; 72 | } 73 | 74 | } // namespace noteahead 75 | -------------------------------------------------------------------------------- /src/domain/line.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef LINE_HPP 17 | #define LINE_HPP 18 | 19 | #include "line_event.hpp" 20 | #include "note_data.hpp" 21 | 22 | #include 23 | #include 24 | 25 | class QXmlStreamReader; 26 | class QXmlStreamWriter; 27 | 28 | namespace noteahead { 29 | 30 | class Line 31 | { 32 | public: 33 | explicit Line(size_t index); 34 | Line(size_t index, const NoteData & noteData); 35 | 36 | size_t index() const; 37 | void setIndex(size_t index); 38 | 39 | void clear(); 40 | bool hasData() const; 41 | 42 | using NoteDataS = std::shared_ptr; 43 | NoteDataS noteData() const; 44 | void setNoteData(const NoteData & noteData); 45 | 46 | using LineEventOpt = std::optional; 47 | LineEventOpt lineEvent() const; 48 | void setLineEvent(LineEventOpt lineEvent); 49 | 50 | void serializeToXml(QXmlStreamWriter & writer) const; 51 | using LineU = std::unique_ptr; 52 | static LineU deserializeFromXml(QXmlStreamReader & reader, size_t trackIndex, size_t columnIndex); 53 | 54 | private: 55 | size_t m_index = 0; 56 | 57 | NoteDataS m_noteData; 58 | 59 | LineEventOpt m_lineEvent; 60 | }; 61 | 62 | } // namespace noteahead 63 | 64 | #endif // LINE_HPP 65 | -------------------------------------------------------------------------------- /src/application/service/midi_worker.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_WORKER_HPP 17 | #define MIDI_WORKER_HPP 18 | 19 | #include 20 | 21 | #include 22 | 23 | class QTimer; 24 | 25 | namespace noteahead { 26 | 27 | class MidiBackend; 28 | 29 | class MidiWorker : public QObject 30 | { 31 | Q_OBJECT 32 | 33 | public: 34 | using MidiBackendS = std::shared_ptr; 35 | explicit MidiWorker(MidiBackendS midiBackend, QString role, QObject * parent = nullptr); 36 | virtual ~MidiWorker() override; 37 | 38 | Q_INVOKABLE void setIsPlaying(bool isPlaying); 39 | 40 | signals: 41 | void portsChanged(QStringList portNames); 42 | void portsAppeared(QStringList portNames); 43 | void portsDisappeared(QStringList portNames); 44 | 45 | void statusTextRequested(QString message); 46 | 47 | protected: 48 | virtual void handlePortsChanged(); 49 | 50 | bool isPlaying() const; 51 | 52 | MidiBackendS midiBackend() const; 53 | 54 | private: 55 | void initializeScanTimer(); 56 | 57 | std::atomic_bool m_isPlaying = false; 58 | std::unique_ptr m_midiScanTimer; 59 | MidiBackendS m_midiBackend; 60 | QString m_role; 61 | }; 62 | 63 | } // namespace noteahead 64 | 65 | #endif // MIDI_WORKER_HPP 66 | -------------------------------------------------------------------------------- /src/infra/midi/implementation/librtmidi/midi_in_rt_midi.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_IN_RT_MIDI_HPP 17 | #define MIDI_IN_RT_MIDI_HPP 18 | 19 | #include "../../midi_backend_in.hpp" 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | namespace noteahead { 27 | 28 | class MidiWorkerIn; 29 | 30 | //! MIDI input backend implementation on the RtMidi library. 31 | class MidiInRtMidi : public MidiBackendIn 32 | { 33 | public: 34 | void updatePorts() override; 35 | 36 | void openPort(MidiPortCR port) override; 37 | void closePort(MidiPortCR port) override; 38 | 39 | std::string midiApiName() const override; 40 | 41 | void clearCallbacks() override; 42 | void setCallbackForPort(const MidiPort & port, InputCallback callback) override; 43 | 44 | PortNameList availablePortNames() const override; 45 | 46 | private: 47 | std::unordered_map> m_openedPorts; 48 | std::unordered_map m_callbacks; 49 | 50 | using Message = std::vector; 51 | using MessageP = Message *; 52 | static void staticCallback(double deltaTime, MessageP message, void * userData); 53 | }; 54 | 55 | } // namespace noteahead 56 | 57 | #endif // MIDI_IN_RT_MIDI_HPP 58 | -------------------------------------------------------------------------------- /src/domain/column_settings.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef COLUMN_SETTINGS_HPP 17 | #define COLUMN_SETTINGS_HPP 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | class QXmlStreamReader; 25 | class QXmlStreamWriter; 26 | 27 | namespace noteahead { 28 | 29 | class ColumnSettings 30 | { 31 | public: 32 | ColumnSettings(); 33 | 34 | struct ChordAutomationSettings 35 | { 36 | struct ChordNote 37 | { 38 | //! Offset (semi-tones relative to the root note). 39 | int8_t offset = 0; 40 | //! Percentage of root note's velocity. 41 | uint8_t velocity = 100; 42 | }; 43 | 44 | ChordNote note1; 45 | ChordNote note2; 46 | ChordNote note3; 47 | 48 | bool isEnabled() const 49 | { 50 | return note1.offset || note2.offset || note3.offset; 51 | } 52 | }; 53 | 54 | ChordAutomationSettings chordAutomationSettings; 55 | 56 | void serializeToXml(QXmlStreamWriter & writer) const; 57 | using ColumnSettingsU = std::unique_ptr; 58 | static ColumnSettingsU deserializeFromXml(QXmlStreamReader & reader); 59 | 60 | QString toString() const; 61 | }; 62 | 63 | } // namespace noteahead 64 | 65 | #endif // COLUMN_SETTINGS_HPP 66 | -------------------------------------------------------------------------------- /src/infra/video/animation.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "animation.hpp" 17 | 18 | #include "../../application/service/mixer_service.hpp" 19 | #include "../../domain/event.hpp" 20 | #include "../../domain/note_data.hpp" 21 | 22 | namespace noteahead { 23 | 24 | Animation::Animation(SongS song, const VideoConfig & config, MixerServiceS mixerService, size_t minTick, size_t maxTick) 25 | : m_song { song } 26 | , m_config { config } 27 | , m_mixerService { mixerService } 28 | , m_minTick { minTick } 29 | , m_maxTick { maxTick } 30 | { 31 | } 32 | 33 | bool Animation::shouldEventPlay(const Event & event) const 34 | { 35 | if (auto && noteData = event.noteData(); noteData) { 36 | return m_mixerService->shouldColumnPlay(noteData->track(), noteData->column()); 37 | } 38 | return true; 39 | } 40 | 41 | size_t Animation::maxTick() const 42 | { 43 | return m_maxTick; 44 | } 45 | 46 | VideoConfig Animation::config() const 47 | { 48 | return m_config; 49 | } 50 | 51 | Animation::SongS Animation::song() const 52 | { 53 | return m_song; 54 | } 55 | 56 | size_t Animation::minTick() const 57 | { 58 | return m_minTick; 59 | } 60 | 61 | Animation::MixerServiceS Animation::mixerService() const 62 | { 63 | return m_mixerService; 64 | } 65 | 66 | Animation::~Animation() = default; 67 | 68 | } // namespace noteahead 69 | -------------------------------------------------------------------------------- /src/unit_tests/track_settings_model_test/track_settings_model_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef TRACK_SETTINGS_MODEL_TEST_HPP 17 | #define TRACK_SETTINGS_MODEL_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class TrackSettingsModelTest : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | private slots: 28 | void test_initialState_shouldHaveExpectedDefaults(); 29 | 30 | void test_setPortName_shouldUpdatePortName(); 31 | void test_setChannel_shouldUpdateChannel(); 32 | void test_setVolumeEnabled_shouldUpdateAndEmitSignal(); 33 | void test_setTrackIndex_shouldUpdateTrackIndex(); 34 | void test_setInstrumentData_shouldUpdateRelevantFields(); 35 | 36 | void test_toInstrument_shouldReturnInstrumentWithDefaultSettings(); 37 | void test_toInstrument_shouldApplyPatchWhenPatchEnabled(); 38 | void test_toInstrument_shouldApplyBankSettingsWhenBankEnabled(); 39 | void test_toInstrument_shouldApplyCutoffWhenCutoffEnabled(); 40 | void test_toInstrument_shouldApplyPanWhenPanEnabled(); 41 | void test_toInstrument_shouldApplyVolumeWhenVolumeEnabled(); 42 | void test_toInstrument_shouldApplyMidiClockAndDelayWhenEnabled(); 43 | void test_toInstrument_setMidiCc_shouldEnableMidiCcSetting(); 44 | }; 45 | 46 | } // namespace noteahead 47 | 48 | #endif // TRACK_SETTINGS_MODEL_TEST_HPP 49 | -------------------------------------------------------------------------------- /src/application/models/recent_files_model.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "recent_files_model.hpp" 17 | 18 | #include 19 | 20 | namespace noteahead { 21 | 22 | RecentFilesModel::RecentFilesModel(QObject * parent) 23 | : QAbstractListModel { parent } 24 | { 25 | } 26 | 27 | void RecentFilesModel::setRecentFiles(const QStringList & list) 28 | { 29 | beginResetModel(); 30 | 31 | m_recentFiles = list; 32 | 33 | endResetModel(); 34 | } 35 | 36 | int RecentFilesModel::rowCount(const QModelIndex & parent) const 37 | { 38 | return parent.isValid() ? 0 : static_cast(m_recentFiles.size()); 39 | } 40 | 41 | QVariant RecentFilesModel::data(const QModelIndex & index, int role) const 42 | { 43 | if (index.isValid() && index.row() >= 0 && index.row() < m_recentFiles.size()) { 44 | const auto path = m_recentFiles.at(index.row()); 45 | switch (static_cast(role)) { 46 | case Role::FilePath: 47 | return path; 48 | case Role::Exists: 49 | return QFileInfo::exists(path); 50 | } 51 | } 52 | return {}; 53 | } 54 | 55 | QHash RecentFilesModel::roleNames() const 56 | { 57 | return { 58 | { static_cast(Role::FilePath), "filePath" }, 59 | { static_cast(Role::Exists), "exists" } 60 | }; 61 | } 62 | 63 | } // namespace noteahead 64 | -------------------------------------------------------------------------------- /src/view/qml/Editor/TrackHeader_ColumnButtons.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import ".." 5 | import "../ToolBar" 6 | 7 | Item { 8 | id: rootItem 9 | signal columnDeletionRequested 10 | signal newColumnRequested 11 | ToolBarButtonBase { 12 | id: addColumnButton 13 | anchors.top: parent.top 14 | anchors.horizontalCenter: parent.horizontalCenter 15 | width: parent.height / 2 16 | height: width 17 | enabled: !UiService.isPlaying() 18 | onClicked: { 19 | rootItem.newColumnRequested(); 20 | focus = false; 21 | } 22 | Keys.onPressed: event => { 23 | if (event.key === Qt.Key_Space) { 24 | event.accepted = true; 25 | } 26 | } 27 | ToolTip.delay: Constants.toolTipDelay 28 | ToolTip.timeout: Constants.toolTipTimeout 29 | ToolTip.visible: hovered 30 | ToolTip.text: qsTr("Add a new note column") 31 | Component.onCompleted: { 32 | setScale(0.9); 33 | setImageSource("../Graphics/add_box.svg"); 34 | } 35 | } 36 | ToolBarButtonBase { 37 | id: removeColumnButton 38 | anchors.top: addColumnButton.bottom 39 | anchors.horizontalCenter: parent.horizontalCenter 40 | width: parent.height / 2 41 | height: width 42 | enabled: !UiService.isPlaying() 43 | onClicked: { 44 | rootItem.columnDeletionRequested(); 45 | focus = false; 46 | } 47 | Keys.onPressed: event => { 48 | if (event.key === Qt.Key_Space) { 49 | event.accepted = true; 50 | } 51 | } 52 | ToolTip.delay: Constants.toolTipDelay 53 | ToolTip.timeout: Constants.toolTipTimeout 54 | ToolTip.visible: hovered 55 | ToolTip.text: qsTr("Remove the last note column") 56 | Component.onCompleted: { 57 | setScale(0.9); 58 | setImageSource("../Graphics/del_box.svg"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/common/utils.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef UTILS_HPP 17 | #define UTILS_HPP 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | class QXmlStreamReader; 29 | 30 | namespace noteahead::Utils { 31 | namespace Misc { 32 | void ensureFileExists(const std::filesystem::path & filePath); 33 | QStringList stdStringVectorToQStringList(const std::vector & stringVector); 34 | std::optional parseDecimal(std::string_view string); 35 | } // namespace Misc 36 | namespace Midi { 37 | double portNameMatchScore(const std::string & s1, const std::string & s2); 38 | } // namespace Midi 39 | namespace Xml { 40 | std::optional readBoolAttribute(QXmlStreamReader & reader, QString name, bool required = true); 41 | std::optional readIntAttribute(QXmlStreamReader & reader, QString name, bool required = true); 42 | std::optional readUIntAttribute(QXmlStreamReader & reader, QString name, bool required = true); 43 | std::optional readStringAttribute(QXmlStreamReader & reader, QString name, bool required = true); 44 | std::optional readMSecAttribute(QXmlStreamReader & reader, QString name, bool required = true); 45 | } // namespace Xml 46 | } // namespace noteahead::Utils 47 | 48 | #endif // UTILS_HPP 49 | -------------------------------------------------------------------------------- /src/infra/midi/midi_backend.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_BACKEND_HPP 17 | #define MIDI_BACKEND_HPP 18 | 19 | #include "midi_port.hpp" 20 | 21 | #include 22 | #include 23 | 24 | namespace noteahead { 25 | 26 | class MidiBackend 27 | { 28 | public: 29 | MidiBackend(); 30 | virtual ~MidiBackend(); 31 | 32 | using PortList = std::vector; 33 | virtual PortList ports() const; 34 | virtual void updatePorts(); 35 | using PortNameList = std::vector; 36 | virtual PortNameList portNames() const; 37 | virtual PortNameList availablePortNames() const; 38 | 39 | virtual MidiPortS portByIndex(size_t index) const; 40 | virtual MidiPortS portByName(const std::string & name) const; 41 | 42 | //! \returns e.g. "ALSA" 43 | virtual std::string midiApiName() const; 44 | 45 | using MidiPortCR = const MidiPort &; 46 | virtual void openPort(MidiPortCR port); 47 | virtual void closePort(MidiPortCR port); 48 | 49 | protected: 50 | void setPorts(PortList devices); 51 | void invalidatePortNameCache(); 52 | 53 | private: 54 | void initializeScanTimer(); 55 | 56 | PortList m_ports; 57 | 58 | using NameToPort = std::unordered_map; 59 | mutable NameToPort m_nameToPortCache; 60 | }; 61 | 62 | } // namespace noteahead 63 | 64 | #endif // MIDI_BACKEND_HPP 65 | -------------------------------------------------------------------------------- /cmake/DebianPackagingDefaultQt.cmake: -------------------------------------------------------------------------------- 1 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION bin) 2 | install(FILES ${CMAKE_SOURCE_DIR}/data/linux/noteahead.desktop DESTINATION share/applications) 3 | install(FILES ${CMAKE_SOURCE_DIR}/data/linux/noteahead.appdata.xml DESTINATION share/metainfo) 4 | install(FILES ${CMAKE_SOURCE_DIR}/data/linux/noteahead.png DESTINATION share/pixmaps) 5 | install(FILES ${CMAKE_SOURCE_DIR}/data/linux/noteahead.png DESTINATION share/icons/hicolor/256x256/apps) 6 | 7 | # Set up CPack for Debian packaging 8 | set(CPACK_PACKAGE_NAME "noteahead") 9 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Noteahead MIDI Tracker") 10 | set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) 11 | set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) 12 | set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) 13 | set(CPACK_PACKAGE_VENDOR Juzzlin) 14 | set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) 15 | set(CPACK_VERBATIM_VARIABLES ON) 16 | 17 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Jussi Lind ") 18 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") 19 | set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS YES) 20 | set(QML_RUNTIME_PACKAGES 21 | qml6-module-qtcore 22 | qml6-module-qtqml 23 | qml6-module-qtqml-workerscript 24 | qml6-module-qtquick 25 | qml6-module-qtquick-controls 26 | qml6-module-qtquick-dialogs 27 | qml6-module-qtquick-layouts 28 | qml6-module-qtquick-templates 29 | qml6-module-qtquick-window 30 | ) 31 | set(CPACK_DEBIAN_PACKAGE_DEPENDS ${QML_RUNTIME_PACKAGES}) 32 | set(CPACK_DEBIAN_PACKAGE_VERSION ${APPLICATION_VERSION}) 33 | set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") 34 | 35 | if(DISTRO_VERSION) 36 | message(STATUS "Distro version: ${DISTRO_VERSION}") 37 | set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-${DISTRO_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}) 38 | else() 39 | set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}) 40 | endif() 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/application/models/note_column_line_container_helper.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef NOTE_COLUMN_LINE_CONTAINER_HELPER_HPP 17 | #define NOTE_COLUMN_LINE_CONTAINER_HELPER_HPP 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | class AutomationService; 26 | class EditorService; 27 | class SelectionService; 28 | class UtilService; 29 | 30 | class NoteColumnLineContainerHelper : public QObject 31 | { 32 | Q_OBJECT 33 | 34 | public: 35 | using AutomationServiceS = std::shared_ptr; 36 | using EditorServiceS = std::shared_ptr; 37 | using SelectionServiceS = std::shared_ptr; 38 | using UtilServiceS = std::shared_ptr; 39 | explicit NoteColumnLineContainerHelper( 40 | AutomationServiceS automationService, EditorServiceS editorService, SelectionServiceS selectionService, UtilServiceS utilService, QObject * parent = nullptr); 41 | 42 | Q_INVOKABLE QList lineColorAndBorderWidth(quint64 patternIndex, quint64 trackIndex, quint64 columnIndex, quint64 lineIndex) const; 43 | 44 | private: 45 | AutomationServiceS m_automationService; 46 | EditorServiceS m_editorService; 47 | SelectionServiceS m_selectionService; 48 | UtilServiceS m_utilService; 49 | }; 50 | 51 | } // namespace noteahead 52 | 53 | #endif // NOTE_COLUMN_LINE_CONTAINER_HELPER_HPP 54 | -------------------------------------------------------------------------------- /src/application/models/midi_cc_selection_model.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef MIDI_CC_SELECTION_MODEL_HPP 17 | #define MIDI_CC_SELECTION_MODEL_HPP 18 | 19 | #include 20 | 21 | #include "../../domain/midi_cc_setting.hpp" 22 | 23 | namespace noteahead { 24 | 25 | class MidiCcSelectionModel : public QObject 26 | { 27 | Q_OBJECT 28 | 29 | Q_PROPERTY(quint8 midiCcSlots READ midiCcSlots NOTIFY midiCcSlotsChanged) 30 | 31 | public: 32 | explicit MidiCcSelectionModel(QObject * parent = nullptr); 33 | 34 | Q_INVOKABLE quint8 midiCcController(quint8 index) const; 35 | Q_INVOKABLE void setMidiCcController(quint8 index, quint8 controller); 36 | 37 | Q_INVOKABLE quint8 midiCcValue(quint8 index) const; 38 | Q_INVOKABLE void setMidiCcValue(quint8 index, quint8 value); 39 | 40 | Q_INVOKABLE bool midiCcEnabled(quint8 index) const; 41 | Q_INVOKABLE void setMidiCcEnabled(quint8 index, bool enabled); 42 | 43 | Q_INVOKABLE QString midiCcToString(quint8 controller) const; 44 | 45 | quint8 midiCcSlots() const; 46 | 47 | using MidiCcSettingList = std::vector; 48 | MidiCcSettingList midiCcSettings() const; 49 | void setMidiCcSettings(const MidiCcSettingList & midiCcSettings); 50 | 51 | signals: 52 | void midiCcSlotsChanged(); 53 | 54 | private: 55 | std::map m_indexToSetting; 56 | }; 57 | 58 | } // namespace noteahead 59 | 60 | #endif // MIDI_CC_SELECTION_MODEL_HPP 61 | -------------------------------------------------------------------------------- /src/unit_tests/automation_service_test/automation_service_test.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef AUTOMATION_SERVICE_TEST_HPP 17 | #define AUTOMATION_SERVICE_TEST_HPP 18 | 19 | #include 20 | 21 | namespace noteahead { 22 | 23 | class AutomationServiceTest : public QObject 24 | { 25 | Q_OBJECT 26 | 27 | private slots: 28 | 29 | void initTestCase(); 30 | 31 | void test_addMidiCcAutomation_shouldAddAutomation(); 32 | void test_deleteMidiCcAutomation_shouldDeleteAutomation(); 33 | void test_addPitchBendAutomation_shouldAddAutomation(); 34 | void test_deletePitchBendAutomation_shouldDeleteAutomation(); 35 | 36 | void test_automationWeight_midiCc_shouldCalculateCorrectWeight(); 37 | void test_automationWeight_pitchBendUp_shouldCalculateCorrectWeight(); 38 | void test_automationWeight_pitchBendDown_shouldCalculateCorrectWeight(); 39 | 40 | void test_renderToEventsByLine_shouldRenderToEvents(); 41 | void test_renderToEventsByLine_disableAutomation_shouldNotRenderEvents(); 42 | void test_renderMidiCcToEventsByLine_withModulation_shouldRenderModulatedEvents(); 43 | void test_renderMidiCcToEventsByLine_withInvertedModulation_shouldRenderModulatedEvents(); 44 | void test_renderToEventsByColumn_shouldRenderToEvents(); 45 | void test_renderToEventsByColumn_shouldPruneRepeatingEvents(); 46 | void test_renderToEventsByColumn_disableAutomation_shouldNotRenderEvents(); 47 | }; 48 | 49 | } // namespace noteahead 50 | 51 | #endif // AUTOMATION_SERVICE_TEST_HPP 52 | -------------------------------------------------------------------------------- /src/infra/video/video_generator.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef VIDEO_GENERATOR_HPP 17 | #define VIDEO_GENERATOR_HPP 18 | 19 | #include "../../domain/event.hpp" 20 | #include "../../domain/song.hpp" 21 | #include "video_config.hpp" 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | class QPainter; 29 | 30 | namespace noteahead { 31 | 32 | class Animation; 33 | class MixerService; 34 | 35 | class VideoGenerator 36 | { 37 | public: 38 | using MixerServiceS = std::shared_ptr; 39 | VideoGenerator(MixerServiceS mixerService); 40 | ~VideoGenerator(); 41 | 42 | using SongS = std::shared_ptr; 43 | void initialize(const VideoConfig & config, SongS song); 44 | void run(); 45 | 46 | private: 47 | void ensureInputFilesExist(const VideoConfig & config); 48 | 49 | void generateVideoFrame(SongS song, const VideoConfig & config, size_t frameIndex, double currentTimeMs); 50 | void generateVideoFrames(SongS song, const VideoConfig & config); 51 | void renderVideo(const VideoConfig & config); 52 | 53 | void runCommand(const QStringList & args) const; 54 | 55 | MixerServiceS m_mixerService; 56 | 57 | SongS m_song; 58 | 59 | using EventMap = std::unordered_map; 60 | EventMap m_eventMap; 61 | 62 | std::unique_ptr m_animation; 63 | 64 | VideoConfig m_config; 65 | 66 | size_t m_minTick = 0; 67 | size_t m_maxTick = 0; 68 | }; 69 | 70 | } // namespace noteahead 71 | 72 | #endif // VIDEO_GENERATOR_HPP 73 | -------------------------------------------------------------------------------- /src/view/qml/Dialogs/SettingsDialog_AudioSettings.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Controls 2.15 3 | import QtQuick.Controls.Universal 2.15 4 | import QtQuick.Layouts 5 | import ".." 6 | import "../Components" 7 | 8 | GroupBox { 9 | title: qsTr("Audio") 10 | ColumnLayout { 11 | anchors.fill: parent 12 | spacing: 10 13 | GridLayout { 14 | width: parent.width 15 | CheckBox { 16 | id: enableAudioRecordingCheckbox 17 | text: qsTr("Enable audio recording from default source when playing.\nAudio files will appear next to the current project file.") 18 | checked: settingsService.recordingEnabled() 19 | Layout.row: 0 20 | Layout.fillWidth: true 21 | ToolTip.delay: Constants.toolTipDelay 22 | ToolTip.timeout: Constants.toolTipTimeout 23 | ToolTip.visible: hovered 24 | ToolTip.text: qsTr("Enable/disable audio recording") 25 | onCheckedChanged: settingsService.setRecordingEnabled(checked) 26 | } 27 | LayoutSeparator { 28 | Layout.row: 1 29 | } 30 | Label { 31 | text: qsTr("Buffer size (samples):") 32 | Layout.column: 0 33 | Layout.columnSpan: 2 34 | Layout.row: 2 35 | Layout.fillWidth: true 36 | } 37 | SpinBox { 38 | id: audioBufferSizeSpinBox 39 | from: 32 40 | to: 4096 41 | stepSize: 32 42 | enabled: enableAudioRecordingCheckbox.checked 43 | value: settingsService.audioBufferSize() 44 | Layout.column: 3 45 | Layout.row: 2 46 | Layout.fillWidth: true 47 | editable: true 48 | ToolTip.delay: Constants.toolTipDelay 49 | ToolTip.timeout: Constants.toolTipTimeout 50 | ToolTip.visible: hovered 51 | ToolTip.text: qsTr("Set buffer size for audio recording") 52 | onValueChanged: settingsService.setAudioBufferSize(value) 53 | Keys.onReturnPressed: focus = false 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/application/service/audio_service.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "audio_service.hpp" 17 | 18 | #include "../../contrib/SimpleLogger/src/simple_logger.hpp" 19 | #include "audio_worker.hpp" 20 | 21 | namespace noteahead { 22 | 23 | static const auto TAG = "AudioService"; 24 | 25 | AudioService::AudioService(QObject * parent) 26 | : QObject { parent } 27 | , m_audioWorker { std::make_unique() } 28 | { 29 | initializeWorker(); 30 | } 31 | 32 | void AudioService::initializeWorker() 33 | { 34 | m_audioWorker->moveToThread(&m_audioWorkerThread); 35 | m_audioWorkerThread.start(QThread::HighestPriority); 36 | } 37 | 38 | void AudioService::startRecording(QString filePath, quint32 bufferSize) 39 | { 40 | const auto functionName = "startRecording"; 41 | if (const bool invoked = QMetaObject::invokeMethod(m_audioWorker.get(), functionName, Q_ARG(QString, filePath), Q_ARG(quint32, bufferSize)); !invoked) { 42 | juzzlin::L(TAG).error() << "Invoking a method failed!: " << functionName; 43 | } 44 | } 45 | 46 | void AudioService::stopRecording() 47 | { 48 | const auto functionName = "stopRecording"; 49 | if (const bool invoked = QMetaObject::invokeMethod(m_audioWorker.get(), functionName); !invoked) { 50 | juzzlin::L(TAG).error() << "Invoking a method failed!: " << functionName; 51 | } 52 | } 53 | 54 | AudioService::~AudioService() 55 | { 56 | juzzlin::L(TAG).info() << "Stopping worker threads"; 57 | 58 | m_audioWorkerThread.exit(); 59 | m_audioWorkerThread.wait(); 60 | } 61 | 62 | } // namespace noteahead 63 | -------------------------------------------------------------------------------- /src/unit_tests/side_chain_service_test/side_chain_service_test.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #include "side_chain_service_test.hpp" 17 | 18 | #include "../../application/service/side_chain_service.hpp" 19 | 20 | #include 21 | 22 | namespace noteahead { 23 | 24 | void SideChainServiceTest::test_setAndGet_sideChainSettings_shouldUpdateModel() 25 | { 26 | SideChainService model; 27 | 28 | // 1. Set properties on the model and verify getters 29 | model.setSideChainEnabled(true); 30 | model.setSideChainSourceTrack(1); 31 | model.setSideChainSourceColumn(2); 32 | model.setSideChainLookahead(10); 33 | model.setSideChainRelease(100); 34 | 35 | QCOMPARE(model.sideChainTargetCount(), 2); 36 | 37 | model.setSideChainTargetEnabled(0, true); 38 | model.setSideChainTargetController(0, 80); 39 | model.setSideChainTargetTargetValue(0, 127); 40 | model.setSideChainTargetReleaseValue(0, 0); 41 | 42 | QCOMPARE(model.sideChainEnabled(), true); 43 | QCOMPARE(model.sideChainSourceTrack(), quint8 { 1 }); 44 | QCOMPARE(model.sideChainSourceColumn(), quint8 { 2 }); 45 | QCOMPARE(model.sideChainLookahead(), 10); 46 | QCOMPARE(model.sideChainRelease(), 100); 47 | 48 | QCOMPARE(model.sideChainTargetEnabled(0), true); 49 | QCOMPARE(model.sideChainTargetController(0), quint8 { 80 }); 50 | QCOMPARE(model.sideChainTargetTargetValue(0), quint8 { 127 }); 51 | QCOMPARE(model.sideChainTargetReleaseValue(0), quint8 { 0 }); 52 | } 53 | 54 | } // namespace noteahead 55 | 56 | QTEST_GUILESS_MAIN(noteahead::SideChainServiceTest) 57 | -------------------------------------------------------------------------------- /src/domain/note_data.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2024 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef NOTE_DATA_HPP 17 | #define NOTE_DATA_HPP 18 | 19 | #include "event_data.hpp" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | class QXmlStreamWriter; 27 | class QXmlStreamReader; 28 | 29 | namespace noteahead { 30 | 31 | class NoteData : public EventData 32 | { 33 | public: 34 | enum class Type 35 | { 36 | None, 37 | NoteOn, 38 | NoteOff 39 | }; 40 | 41 | NoteData(size_t track, size_t column); 42 | NoteData(); 43 | 44 | void setAsNoteOn(uint8_t note, uint8_t velocity); 45 | void setAsNoteOff(uint8_t note); 46 | void setAsNoteOff(); 47 | 48 | void transpose(int semitones); 49 | 50 | NoteData::Type type() const; 51 | 52 | std::optional note() const; 53 | 54 | uint8_t velocity() const; 55 | void setVelocity(uint8_t velocity); 56 | 57 | uint8_t delay() const; 58 | void setDelay(uint8_t ticks); 59 | 60 | std::string toString() const; 61 | void serializeToXml(QXmlStreamWriter & writer) const; 62 | using NoteDataS = std::shared_ptr; 63 | static NoteDataS deserializeFromXml(QXmlStreamReader & reader, size_t trackIndex, size_t columnIndex); 64 | 65 | size_t track() const; 66 | size_t column() const; 67 | 68 | private: 69 | Type m_type = Type::None; 70 | 71 | std::optional m_note; 72 | std::optional m_delay; // In ticks per line 73 | uint8_t m_velocity = 0; 74 | }; 75 | 76 | } // namespace noteahead 77 | 78 | #endif // NOTE_DATA_HPP 79 | -------------------------------------------------------------------------------- /src/infra/video/bars_animation.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef BARS_ANIMATION_H 17 | #define BARS_ANIMATION_H 18 | 19 | #include "animation.hpp" 20 | 21 | #include 22 | 23 | namespace noteahead { 24 | 25 | class BarsAnimation : public Animation 26 | { 27 | public: 28 | explicit BarsAnimation(SongS song, const VideoConfig & config, MixerServiceS mixerService, size_t minTick, size_t maxTick); 29 | 30 | void generateAnimationFrames(const EventMap & events) override; 31 | 32 | void renderAnimationFrame(QPainter & painter, size_t frameIndex, double currentTimeMs) override; 33 | 34 | private: 35 | void integrate(AnimationFrame & animationFrame); 36 | 37 | BarsAnimation::AnimationFrame::Particle createFlashParticle() const; 38 | BarsAnimation::AnimationFrame::Particle createNoteParticle(double x, double y, int note, double velocity, size_t track) const; 39 | BarsAnimation::AnimationFrame::Particle createPrimaryParticle(double x, double y, int note, double velocity) const; 40 | 41 | void renderParticles(QPainter & painter, AnimationFrame & animationFrame) const; 42 | void renderNoteParticle(QPainter & painter, AnimationFrame::Particle & particle) const; 43 | void renderFlashParticle(QPainter & painter, AnimationFrame::Particle & particle) const; 44 | 45 | using AnimationFrameS = std::shared_ptr; 46 | using TickToAnimationFrameMap = std::map; 47 | TickToAnimationFrameMap m_animationFrames; 48 | 49 | std::unique_ptr m_animation; 50 | }; 51 | 52 | } // namespace noteahead 53 | 54 | #endif // BARS_ANIMATION_H 55 | -------------------------------------------------------------------------------- /src/infra/video/video_config.hpp: -------------------------------------------------------------------------------- 1 | // This file is part of Noteahead. 2 | // Copyright (C) 2025 Jussi Lind 3 | // 4 | // Noteahead is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // Noteahead is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Noteahead. If not, see . 15 | 16 | #ifndef VIDEO_CONFIG_HPP 17 | #define VIDEO_CONFIG_HPP 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | namespace noteahead { 26 | 27 | struct VideoConfig 28 | { 29 | int width = 1920; 30 | int height = 1080; 31 | 32 | size_t fps = 60; 33 | 34 | std::string ffmpegPath = "ffmpeg"; 35 | std::string audioCodec = "libfdk_aac"; 36 | std::string videoCodec = "libx264"; 37 | 38 | std::string audioPath; 39 | std::string songPath; 40 | 41 | std::string imagePath; 42 | double imageOpacity = 0.5; 43 | double imageZoomSpeed = 0; 44 | double imageRotationSpeed = 0; 45 | QImage image; 46 | 47 | std::optional flashTrackName; 48 | std::optional flashColumnName; 49 | 50 | double trackOpacity = 0.25; 51 | 52 | std::string logoPath; 53 | int logoX = 16; 54 | int logoY = 16; 55 | QImage logo; 56 | double logoFadeFactor = 1.0; 57 | 58 | std::string outputDir = "Frames-" + std::to_string(QDateTime::currentSecsSinceEpoch()); 59 | 60 | size_t startPosition = 0; 61 | std::chrono::milliseconds leadInTime; 62 | std::chrono::milliseconds leadOutTime; 63 | std::optional length; 64 | 65 | std::string scrollingText; 66 | double scrollingTextOpacity = 0.5; 67 | 68 | enum class Type 69 | { 70 | Default, 71 | Bars 72 | }; 73 | 74 | Type type = Type::Default; 75 | }; 76 | 77 | } // namespace noteahead 78 | 79 | #endif // VIDEO_CONFIG_HPP 80 | --------------------------------------------------------------------------------