commit du code source
This commit is contained in:
@@ -32,3 +32,6 @@
|
|||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
JuceLibraryCode/**
|
||||||
|
|
||||||
|
/.vs
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,229 @@
|
|||||||
|
# Automatically generated makefile, created by the Projucer
|
||||||
|
# Don't edit this file! Your changes will be overwritten when you re-save the Projucer project!
|
||||||
|
|
||||||
|
# build with "V=1" for verbose builds
|
||||||
|
ifeq ($(V), 1)
|
||||||
|
V_AT =
|
||||||
|
else
|
||||||
|
V_AT = @
|
||||||
|
endif
|
||||||
|
|
||||||
|
# (this disables dependency generation if multiple architectures are set)
|
||||||
|
DEPFLAGS := $(if $(word 2, $(TARGET_ARCH)), , -MMD)
|
||||||
|
|
||||||
|
ifndef PKG_CONFIG
|
||||||
|
PKG_CONFIG=pkg-config
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifndef STRIP
|
||||||
|
STRIP=strip
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifndef AR
|
||||||
|
AR=ar
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifndef CONFIG
|
||||||
|
CONFIG=Debug
|
||||||
|
endif
|
||||||
|
|
||||||
|
JUCE_ARCH_LABEL := $(shell uname -m)
|
||||||
|
|
||||||
|
ifeq ($(CONFIG),Debug)
|
||||||
|
JUCE_BINDIR := build
|
||||||
|
JUCE_LIBDIR := build
|
||||||
|
JUCE_OBJDIR := build/intermediate/Debug
|
||||||
|
JUCE_OUTDIR := build
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH),)
|
||||||
|
TARGET_ARCH :=
|
||||||
|
endif
|
||||||
|
|
||||||
|
JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DJUCE_DISPLAY_SPLASH_SCREEN=1" "-DJUCE_USE_DARK_SPLASH_SCREEN=0" "-DJUCE_PROJUCER_VERSION=0x70003" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_JACK=1" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCER_LINUX_MAKE_B28D=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000" $(shell $(PKG_CONFIG) --cflags alsa freetype2 libcurl webkit2gtk-4.0 gtk+-x11-3.0) -pthread -IJuceLibraryCode -IJuceLibraryCode/modules $(CPPFLAGS)
|
||||||
|
JUCE_CPPFLAGS_APP := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0"
|
||||||
|
JUCE_TARGET_APP := Tchick
|
||||||
|
|
||||||
|
JUCE_CFLAGS += $(JUCE_CPPFLAGS) $(TARGET_ARCH) -g -ggdb -O0 $(CFLAGS)
|
||||||
|
JUCE_CXXFLAGS += $(JUCE_CFLAGS) -std=c++20 $(CXXFLAGS)
|
||||||
|
JUCE_LDFLAGS += $(TARGET_ARCH) -L$(JUCE_BINDIR) -L$(JUCE_LIBDIR) $(shell $(PKG_CONFIG) --libs alsa freetype2 libcurl) -fvisibility=hidden -lrt -ldl -lpthread $(LDFLAGS)
|
||||||
|
|
||||||
|
CLEANCMD = rm -rf $(JUCE_OUTDIR)/$(TARGET) $(JUCE_OBJDIR)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(CONFIG),Release)
|
||||||
|
JUCE_BINDIR := build
|
||||||
|
JUCE_LIBDIR := build
|
||||||
|
JUCE_OBJDIR := build/intermediate/Release
|
||||||
|
JUCE_OUTDIR := build
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH),)
|
||||||
|
TARGET_ARCH :=
|
||||||
|
endif
|
||||||
|
|
||||||
|
JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DJUCE_DISPLAY_SPLASH_SCREEN=1" "-DJUCE_USE_DARK_SPLASH_SCREEN=0" "-DJUCE_PROJUCER_VERSION=0x70003" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_JACK=1" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCER_LINUX_MAKE_B28D=1" "-DJUCE_APP_VERSION=1.0.0" "-DJUCE_APP_VERSION_HEX=0x10000" $(shell $(PKG_CONFIG) --cflags alsa freetype2 libcurl webkit2gtk-4.0 gtk+-x11-3.0) -pthread -IJuceLibraryCode -IJuceLibraryCode/modules $(CPPFLAGS)
|
||||||
|
JUCE_CPPFLAGS_APP := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0"
|
||||||
|
JUCE_TARGET_APP := Tchick
|
||||||
|
|
||||||
|
JUCE_CFLAGS += $(JUCE_CPPFLAGS) $(TARGET_ARCH) -O3 $(CFLAGS)
|
||||||
|
JUCE_CXXFLAGS += $(JUCE_CFLAGS) -std=c++20 $(CXXFLAGS)
|
||||||
|
JUCE_LDFLAGS += $(TARGET_ARCH) -L$(JUCE_BINDIR) -L$(JUCE_LIBDIR) $(shell $(PKG_CONFIG) --libs alsa freetype2 libcurl) -fvisibility=hidden -lrt -ldl -lpthread $(LDFLAGS)
|
||||||
|
|
||||||
|
CLEANCMD = rm -rf $(JUCE_OUTDIR)/$(TARGET) $(JUCE_OBJDIR)
|
||||||
|
endif
|
||||||
|
|
||||||
|
OBJECTS_APP := \
|
||||||
|
$(JUCE_OBJDIR)/OptionsPanel_74384d2f.o \
|
||||||
|
$(JUCE_OBJDIR)/TempoPanel_68242652.o \
|
||||||
|
$(JUCE_OBJDIR)/PlayPanel_f7a1c6f1.o \
|
||||||
|
$(JUCE_OBJDIR)/MesurePanel_621c6998.o \
|
||||||
|
$(JUCE_OBJDIR)/Config_df6c736b.o \
|
||||||
|
$(JUCE_OBJDIR)/BPMPanel_fcde4d0e.o \
|
||||||
|
$(JUCE_OBJDIR)/Main_b18eef62.o \
|
||||||
|
$(JUCE_OBJDIR)/Metronome_d2176e73.o \
|
||||||
|
$(JUCE_OBJDIR)/MainComponent_fec15f05.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_basics_854129ea.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_devices_c670bf62.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_formats_7957c261.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_processors_46d0f806.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_processors_ara_80e36097.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_processors_lv2_libs_badcdc68.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_utils_d9446d36.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_core_f75b497b.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_data_structures_7e723c43.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_events_92002035.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_graphics_915442e7.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_gui_basics_1fa21125.o \
|
||||||
|
$(JUCE_OBJDIR)/include_juce_gui_extra_fc3dee7a.o \
|
||||||
|
|
||||||
|
.PHONY: clean all strip
|
||||||
|
|
||||||
|
all : $(JUCE_OUTDIR)/$(JUCE_TARGET_APP)
|
||||||
|
|
||||||
|
$(JUCE_OUTDIR)/$(JUCE_TARGET_APP) : $(OBJECTS_APP) $(RESOURCES)
|
||||||
|
@command -v $(PKG_CONFIG) >/dev/null 2>&1 || { echo >&2 "pkg-config not installed. Please, install it."; exit 1; }
|
||||||
|
@$(PKG_CONFIG) --print-errors alsa freetype2 libcurl
|
||||||
|
@echo Linking "Tchick - App"
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_BINDIR)
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_LIBDIR)
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OUTDIR)
|
||||||
|
$(V_AT)$(CXX) -o $(JUCE_OUTDIR)/$(JUCE_TARGET_APP) $(OBJECTS_APP) $(JUCE_LDFLAGS) $(JUCE_LDFLAGS_APP) $(RESOURCES) $(TARGET_ARCH)
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/OptionsPanel_74384d2f.o: Source/OptionsPanel.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling OptionsPanel.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/TempoPanel_68242652.o: Source/TempoPanel.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling TempoPanel.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/PlayPanel_f7a1c6f1.o: Source/PlayPanel.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling PlayPanel.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/MesurePanel_621c6998.o: Source/MesurePanel.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling MesurePanel.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/Config_df6c736b.o: Source/Config.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling Config.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/BPMPanel_fcde4d0e.o: Source/BPMPanel.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling BPMPanel.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/Main_b18eef62.o: Source/Main.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling Main.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/Metronome_d2176e73.o: Source/Metronome.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling Metronome.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/MainComponent_fec15f05.o: Source/MainComponent.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling MainComponent.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_basics_854129ea.o: JuceLibraryCode/include_juce_audio_basics.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_audio_basics.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_devices_c670bf62.o: JuceLibraryCode/include_juce_audio_devices.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_audio_devices.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_formats_7957c261.o: JuceLibraryCode/include_juce_audio_formats.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_audio_formats.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_processors_46d0f806.o: JuceLibraryCode/include_juce_audio_processors.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_audio_processors.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_processors_ara_80e36097.o: JuceLibraryCode/include_juce_audio_processors_ara.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_audio_processors_ara.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_processors_lv2_libs_badcdc68.o: JuceLibraryCode/include_juce_audio_processors_lv2_libs.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_audio_processors_lv2_libs.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_audio_utils_d9446d36.o: JuceLibraryCode/include_juce_audio_utils.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_audio_utils.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_core_f75b497b.o: JuceLibraryCode/include_juce_core.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_core.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_data_structures_7e723c43.o: JuceLibraryCode/include_juce_data_structures.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_data_structures.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_events_92002035.o: JuceLibraryCode/include_juce_events.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_events.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_graphics_915442e7.o: JuceLibraryCode/include_juce_graphics.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_graphics.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_gui_basics_1fa21125.o: JuceLibraryCode/include_juce_gui_basics.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_gui_basics.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
$(JUCE_OBJDIR)/include_juce_gui_extra_fc3dee7a.o: JuceLibraryCode/include_juce_gui_extra.cpp
|
||||||
|
-$(V_AT)mkdir -p $(JUCE_OBJDIR)
|
||||||
|
@echo "Compiling include_juce_gui_extra.cpp"
|
||||||
|
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo Cleaning Tchick
|
||||||
|
$(V_AT)$(CLEANCMD)
|
||||||
|
|
||||||
|
strip:
|
||||||
|
@echo Stripping Tchick
|
||||||
|
-$(V_AT)$(STRIP) --strip-unneeded $(JUCE_OUTDIR)/$(TARGET)
|
||||||
|
|
||||||
|
-include $(OBJECTS_APP:%.o=%.d)
|
||||||
@@ -1,3 +1,12 @@
|
|||||||
# Tchick
|
# Tchick
|
||||||
|
|
||||||
Simple metronome
|
A basic metronome application built to test capabilities of [JUCE](https://github.com/juce-framework/JUCE)
|
||||||
|
audio application framework.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
Generate your prefered build chain via Projucer by opening Tchick.sln
|
||||||
|
Visual Studio 2022 and linux makefiles are provided as examples.
|
||||||
|
On linux, you have to copy Assets/Clicks folder in the output directory.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
Run the executable. Click play button. Have fun.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
name: New MCP server
|
||||||
|
version: 0.0.1
|
||||||
|
schema: v1
|
||||||
|
mcpServers:
|
||||||
|
- name: New MCP server
|
||||||
|
command: npx
|
||||||
|
args:
|
||||||
|
- -y
|
||||||
|
- <your-mcp-server>
|
||||||
|
env: {}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
description: A description of your rule
|
||||||
|
---
|
||||||
|
|
||||||
|
Your rule content
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"continue.enableConsole": true,
|
||||||
|
"continue.enableQuickActions": true,
|
||||||
|
"continue.telemetryEnabled": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion du tempo.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BPMPanel.h"
|
||||||
|
|
||||||
|
|
||||||
|
BPMPanel::BPMPanel(juce::Colour c) : backgroundColour(c)
|
||||||
|
{
|
||||||
|
// initialisation de l'interface
|
||||||
|
addComponent(decrement);
|
||||||
|
addComponent(bpm);
|
||||||
|
addComponent(increment);
|
||||||
|
|
||||||
|
addComponent(twice);
|
||||||
|
addComponent(half);
|
||||||
|
addComponent(set60);
|
||||||
|
addComponent(set90);
|
||||||
|
addComponent(set120);
|
||||||
|
addComponent(set150);
|
||||||
|
|
||||||
|
decrement.setRepeatSpeed(300, 100); // On veux pouvoir laisser le bouton appuyé
|
||||||
|
increment.setRepeatSpeed(300, 100);
|
||||||
|
|
||||||
|
bpm.setText("72", juce::dontSendNotification);
|
||||||
|
bpm.getFont().setBold(true);
|
||||||
|
bpm.setColour(juce::Label::textColourId, juce::Colours::black);
|
||||||
|
bpm.setJustificationType(juce::Justification::centred);
|
||||||
|
bpm.setFont(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPMPanel::addComponent(juce::Component& c)
|
||||||
|
{
|
||||||
|
addAndMakeVisible(c);
|
||||||
|
c.setColour(juce::TextButton::buttonColourId, backgroundColour);
|
||||||
|
c.setColour(juce::TextButton::textColourOffId, juce::Colours::black);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPMPanel::mouseWheelMove(const juce::MouseEvent&,
|
||||||
|
const juce::MouseWheelDetails& wheel)
|
||||||
|
{
|
||||||
|
// capture des mouseWheel pour invoquer increment/decrement
|
||||||
|
float dir = wheel.deltaY * (wheel.isReversed ? -1.0f : 1.0f);
|
||||||
|
if (dir > 0)
|
||||||
|
{
|
||||||
|
increment.onClick();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decrement.onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPMPanel::paint(juce::Graphics& g)
|
||||||
|
{
|
||||||
|
g.fillAll(backgroundColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPMPanel::resized()
|
||||||
|
{
|
||||||
|
// Layout à la mano avec une première ligne pour les raccourcis
|
||||||
|
auto area = getLocalBounds();
|
||||||
|
int margin = 3;
|
||||||
|
area.removeFromTop(3*margin);
|
||||||
|
|
||||||
|
{
|
||||||
|
// première ligne accès rapide aux speeds
|
||||||
|
int setLinHeigth = 20;
|
||||||
|
auto setSpeed = area.removeFromTop(setLinHeigth);
|
||||||
|
int butWidth = 64;
|
||||||
|
int marginH = (setSpeed.getWidth() - butWidth * 4) / 2;
|
||||||
|
setSpeed.removeFromLeft(marginH);
|
||||||
|
set60.setBounds(setSpeed.removeFromLeft(butWidth)); setSpeed.removeFromLeft(margin);
|
||||||
|
set90.setBounds(setSpeed.removeFromLeft(butWidth)); setSpeed.removeFromLeft(margin);
|
||||||
|
set120.setBounds(setSpeed.removeFromLeft(butWidth)); setSpeed.removeFromLeft(margin);
|
||||||
|
set150.setBounds(setSpeed.removeFromLeft(butWidth)); setSpeed.removeFromLeft(margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
area.removeFromTop(margin);
|
||||||
|
|
||||||
|
// Et une deuxième ligne avec increment/decrement et le tempo courant
|
||||||
|
{
|
||||||
|
// deuxième ligne BPM inc + dec
|
||||||
|
int butHeigth = 20;
|
||||||
|
int butWidth = 64;
|
||||||
|
int labeSize = 5 * butHeigth;
|
||||||
|
int totalWidth = 2 * butWidth + labeSize;
|
||||||
|
int marginH = (area.getWidth() - totalWidth) / 2;
|
||||||
|
int marginV = (area.getHeight() - labeSize) / 2;
|
||||||
|
decrement.setBounds(area.getX() + marginH, area.getY() + marginV + butHeigth, butWidth, 2 * butHeigth);
|
||||||
|
half.setBounds(area.getX() + marginH, area.getY() + marginV + 3 * butHeigth + margin, butWidth, butHeigth);
|
||||||
|
|
||||||
|
bpm.setBounds(area.getX() + marginH + butWidth, area.getY() + marginV, labeSize, labeSize);
|
||||||
|
|
||||||
|
increment.setBounds(area.getX() + marginH + butWidth + labeSize, area.getY() + marginV + butHeigth, butWidth, 2 * butHeigth);
|
||||||
|
twice.setBounds(area.getX() + marginH + butWidth + labeSize, area.getY() + marginV + 3 * butHeigth + margin, butWidth, butHeigth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPMPanel::init(Metronome & metro)
|
||||||
|
{
|
||||||
|
// On établi les liaisons entre le Metronome et les composants graphique
|
||||||
|
bpm.setText(juce::String(metro.getBPM()), juce::dontSendNotification); // On récupère la valeur initiale du tempo
|
||||||
|
increment.onClick = [&, this]() { inc(metro); };
|
||||||
|
decrement.onClick = [&, this]() { dec(metro); };
|
||||||
|
set60.onClick = [&, this]() { setBPM(metro, 60); };
|
||||||
|
set90.onClick = [&, this]() { setBPM(metro, 90); };
|
||||||
|
set120.onClick = [&, this]() { setBPM(metro, 120); };
|
||||||
|
set150.onClick = [&, this]() { setBPM(metro, 150); };
|
||||||
|
half.onClick = [&, this]() { setBPM(metro, metro.getBPM() / 2); };
|
||||||
|
twice.onClick = [&, this]() { setBPM(metro, metro.getBPM() * 2); };
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPMPanel::inc(Metronome& metro)
|
||||||
|
{
|
||||||
|
// Ajoute 1 au tempo
|
||||||
|
metro.setBPM(metro.getBPM() + 1);
|
||||||
|
bpm.setText(juce::String(metro.getBPM()), juce::dontSendNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPMPanel::dec(Metronome& metro)
|
||||||
|
{
|
||||||
|
// reduit le tempo de 1
|
||||||
|
metro.setBPM(metro.getBPM() - 1);
|
||||||
|
bpm.setText(juce::String(metro.getBPM()), juce::dontSendNotification);
|
||||||
|
}
|
||||||
|
void BPMPanel::setBPM(Metronome& metro, int value)
|
||||||
|
{
|
||||||
|
// défini la nouvelle valeur du tempo
|
||||||
|
metro.setBPM(value);
|
||||||
|
bpm.setText(juce::String(metro.getBPM()), juce::dontSendNotification);
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion du tempo.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "Metronome.h"
|
||||||
|
|
||||||
|
struct BPMPanel : public juce::Component
|
||||||
|
{
|
||||||
|
explicit BPMPanel(juce::Colour c);
|
||||||
|
|
||||||
|
// Méthode de surcharge JUCE
|
||||||
|
void mouseWheelMove(const juce::MouseEvent&,
|
||||||
|
const juce::MouseWheelDetails& wheel) override; // on prend en charge le mouseWheel pour changer le tempo
|
||||||
|
|
||||||
|
void paint(juce::Graphics& g) override;
|
||||||
|
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
// utilitaire graphique
|
||||||
|
void addComponent(juce::Component& c);
|
||||||
|
|
||||||
|
// méthodes de gestion du tempo
|
||||||
|
void init(Metronome& metro);
|
||||||
|
void inc(Metronome& metro);
|
||||||
|
void dec(Metronome& metro);
|
||||||
|
void setBPM(Metronome& metro, int value);
|
||||||
|
|
||||||
|
// composants graphiques
|
||||||
|
juce::Colour backgroundColour;
|
||||||
|
juce::TextButton increment{ "+" };
|
||||||
|
juce::TextButton decrement{ "-" };
|
||||||
|
juce::TextButton twice{ "*2" };
|
||||||
|
juce::TextButton half{ "/2" };
|
||||||
|
juce::TextButton set60{ "60" };
|
||||||
|
juce::TextButton set90{ "90" };
|
||||||
|
juce::TextButton set120{ "120" };
|
||||||
|
juce::TextButton set150{ "150" };
|
||||||
|
juce::Label bpm;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion de la sauvegarde de la configuration courante.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
// petit singleton
|
||||||
|
Config* Config::instance = nullptr;
|
||||||
|
|
||||||
|
|
||||||
|
Config& Config::Load()
|
||||||
|
{
|
||||||
|
if (instance == nullptr)
|
||||||
|
instance = new Config();
|
||||||
|
|
||||||
|
// On cherche le fichier json dans le repertoire courant
|
||||||
|
juce::File curDir{ juce::File::getSpecialLocation(juce::File::SpecialLocationType::currentExecutableFile).getParentDirectory() };
|
||||||
|
juce::File confFile{ curDir.getChildFile("tchick.json") };
|
||||||
|
|
||||||
|
if (confFile.exists())
|
||||||
|
{
|
||||||
|
// s'il existe, on le charge
|
||||||
|
auto content = juce::JSON::parse(confFile);
|
||||||
|
instance->fromVar(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::Save(Metronome& metro, OptionsPanel& options)
|
||||||
|
{
|
||||||
|
// On initialise les paramètres
|
||||||
|
instance->bpm = metro.getBPM();
|
||||||
|
instance->measures = metro.getMeasures();
|
||||||
|
instance->gain = metro.getGain();
|
||||||
|
instance->figure = (int)metro.getFigure();
|
||||||
|
instance->sound = options.getSoundName();
|
||||||
|
|
||||||
|
// on transforme en var
|
||||||
|
juce::var obj = instance->asVar();
|
||||||
|
|
||||||
|
// on met à jours le fichier
|
||||||
|
juce::File curDir{ juce::File::getSpecialLocation(juce::File::SpecialLocationType::currentExecutableFile).getParentDirectory() };
|
||||||
|
juce::File confFile{ curDir.getChildFile("tchick.json") };
|
||||||
|
juce::FileOutputStream stream(confFile);
|
||||||
|
|
||||||
|
if (stream.openedOk())
|
||||||
|
{
|
||||||
|
// méthode préconisé pour ecraser le fichier existant
|
||||||
|
stream.setPosition(0);
|
||||||
|
stream.truncate();
|
||||||
|
juce::JSON::writeToStream(stream, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::var Config::asVar()
|
||||||
|
{
|
||||||
|
// création du var à partir de this
|
||||||
|
juce::DynamicObject* obj = new juce::DynamicObject();
|
||||||
|
juce::NamedValueSet& properties = obj->getProperties();
|
||||||
|
properties.set("bpm", bpm);
|
||||||
|
properties.set("measures", measures);
|
||||||
|
properties.set("gain", gain);
|
||||||
|
properties.set("sound", sound);
|
||||||
|
properties.set("figure", figure);
|
||||||
|
return juce::var(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::fromVar(juce::var var)
|
||||||
|
{
|
||||||
|
// chargement des paramètres depuis un var
|
||||||
|
juce::DynamicObject* obj = var.getDynamicObject();
|
||||||
|
juce::NamedValueSet& properties = obj->getProperties();
|
||||||
|
|
||||||
|
if(properties.contains("bpm")) bpm = properties["bpm"];
|
||||||
|
if(properties.contains("measures")) measures = properties["measures"];
|
||||||
|
if(properties.contains("gain")) gain = properties["gain"];
|
||||||
|
if(properties.contains("sound")) sound = properties["sound"];
|
||||||
|
if(properties.contains("figure")) figure = properties["figure"];
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion de la sauvegarde de la configuration courante.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "Metronome.h"
|
||||||
|
#include "OptionsPanel.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Metronome;
|
||||||
|
struct OptionsPanel;
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~Config() {};
|
||||||
|
|
||||||
|
// charge la config depuis le fichier json ou les valeurs par defaut
|
||||||
|
static Config& Load();
|
||||||
|
|
||||||
|
// enregistre la config dans le fichier json
|
||||||
|
static void Save(Metronome& metro, OptionsPanel& options);
|
||||||
|
|
||||||
|
// paramètres sauvegardés
|
||||||
|
int bpm{ 72 }; // default 72
|
||||||
|
int measures{ 4 }; // default 4
|
||||||
|
float gain{ 1.0f }; // default 1.0
|
||||||
|
juce::String sound{ "" }; // default empty
|
||||||
|
int figure{ 1 }; // default 1 (noire)
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
// je crois que c'est pas suffisant mais on devrait toujours passer par les méthodes statiques
|
||||||
|
Config() {};
|
||||||
|
|
||||||
|
// convertisseur paramètre <-> var
|
||||||
|
juce::var asVar();
|
||||||
|
|
||||||
|
void fromVar(juce::var var);
|
||||||
|
|
||||||
|
// seule instance de config
|
||||||
|
static Config* instance;
|
||||||
|
};
|
||||||
File diff suppressed because one or more lines are too long
+118
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "MainComponent.h"
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class MetronomeApplication : public juce::JUCEApplication
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//==============================================================================
|
||||||
|
MetronomeApplication() {}
|
||||||
|
|
||||||
|
const juce::String getApplicationName() override { return ProjectInfo::projectName; }
|
||||||
|
const juce::String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||||
|
bool moreThanOneInstanceAllowed() override { return true; }
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void initialise (const juce::String&) override
|
||||||
|
{
|
||||||
|
// This method is where you should put your application's initialisation code..
|
||||||
|
|
||||||
|
mainWindow.reset (new MainWindow (getApplicationName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown() override
|
||||||
|
{
|
||||||
|
// Add your application's shutdown code here..
|
||||||
|
|
||||||
|
mainWindow = nullptr; // (deletes our window)
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void systemRequestedQuit() override
|
||||||
|
{
|
||||||
|
// This is called when the app is being asked to quit: you can ignore this
|
||||||
|
// request and let the app carry on running, or call quit() to allow the app to close.
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void anotherInstanceStarted (const juce::String&) override
|
||||||
|
{
|
||||||
|
// When another instance of the app is launched while this one is running,
|
||||||
|
// this method is invoked, and the commandLine parameter tells you what
|
||||||
|
// the other instance's command-line arguments were.
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/*
|
||||||
|
This class implements the desktop window that contains an instance of
|
||||||
|
our MainComponent class.
|
||||||
|
*/
|
||||||
|
class MainWindow : public juce::DocumentWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MainWindow (juce::String name)
|
||||||
|
: DocumentWindow (name,
|
||||||
|
juce::Desktop::getInstance().getDefaultLookAndFeel()
|
||||||
|
.findColour (juce::ResizableWindow::backgroundColourId),
|
||||||
|
DocumentWindow::allButtons)
|
||||||
|
{
|
||||||
|
setUsingNativeTitleBar (true);
|
||||||
|
setContentOwned (new MainComponent(Config::Load()), true); // On charge la configuration json et on la passe au constructeur de MainComponent
|
||||||
|
|
||||||
|
#if JUCE_IOS || JUCE_ANDROID
|
||||||
|
setFullScreen (true);
|
||||||
|
#else
|
||||||
|
setResizable (false, false);
|
||||||
|
centreWithSize (getWidth(), getHeight());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setVisible (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeButtonPressed() override
|
||||||
|
{
|
||||||
|
// This is called when the user tries to close this window. Here, we'll just
|
||||||
|
// ask the app to quit when this happens, but you can change this to do
|
||||||
|
// whatever you need.
|
||||||
|
((MainComponent*)getContentComponent())->saveConfig();
|
||||||
|
JUCEApplication::getInstance()->systemRequestedQuit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: Be careful if you override any DocumentWindow methods - the base
|
||||||
|
class uses a lot of them, so by overriding you might break its functionality.
|
||||||
|
It's best to do all your work in your content component instead, but if
|
||||||
|
you really have to override any DocumentWindow methods, make sure your
|
||||||
|
subclass also calls the superclass's method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private:
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<MainWindow> mainWindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// This macro generates the main() routine that launches the app.
|
||||||
|
START_JUCE_APPLICATION (MetronomeApplication)
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "MainComponent.h"
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
MainComponent::MainComponent(Config& conf) :
|
||||||
|
metronome(conf),
|
||||||
|
measures(juce::Colours::whitesmoke),
|
||||||
|
bpm(juce::Colours::whitesmoke),
|
||||||
|
tempo(juce::Colours::whitesmoke),
|
||||||
|
playButton(juce::Colours::whitesmoke),
|
||||||
|
options(juce::Colours::whitesmoke, conf)
|
||||||
|
{
|
||||||
|
addAndMakeVisible (measures);
|
||||||
|
addAndMakeVisible (bpm);
|
||||||
|
bpm.init(metronome);
|
||||||
|
addAndMakeVisible (tempo);
|
||||||
|
tempo.init(metronome);
|
||||||
|
|
||||||
|
addAndMakeVisible(playButton);
|
||||||
|
playButton.init(metronome);
|
||||||
|
addAndMakeVisible (options);
|
||||||
|
options.init(metronome);
|
||||||
|
|
||||||
|
setSize (400, 600);
|
||||||
|
|
||||||
|
// Some platforms require permissions to open input channels so request that here
|
||||||
|
if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
|
||||||
|
&& ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
|
||||||
|
{
|
||||||
|
juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
|
||||||
|
[&] (bool granted) { if (granted) setAudioChannels (2, 2); });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Specify the number of input and output channels that we want to open
|
||||||
|
setAudioChannels (0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
startTimer(20); // timer pour aller relire les eventuels signaux du métronome
|
||||||
|
}
|
||||||
|
|
||||||
|
MainComponent::~MainComponent()
|
||||||
|
{
|
||||||
|
shutdownAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void MainComponent::prepareToPlay (int , double sampleRate)
|
||||||
|
{
|
||||||
|
metronome.prepareToPlay(sampleRate); // passe le sampleRate au métronome
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainComponent::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
|
||||||
|
{
|
||||||
|
// netoyage de l'entrée
|
||||||
|
bufferToFill.clearActiveBufferRegion();
|
||||||
|
|
||||||
|
if (playButton.playState == PlayState::playing)
|
||||||
|
{
|
||||||
|
// boucle de lecture du métronome
|
||||||
|
metronome.getNextAudioBlock (bufferToFill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainComponent::releaseResources()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void MainComponent::paint (juce::Graphics& g)
|
||||||
|
{
|
||||||
|
g.fillAll (juce::Colours::lightgrey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainComponent::resized()
|
||||||
|
{
|
||||||
|
// Comme on à séparé tout en composants, on utilise un simple grille en ligne
|
||||||
|
juce::Grid grid;
|
||||||
|
using Track = juce::Grid::TrackInfo;
|
||||||
|
using Fr = juce::Grid::Fr;
|
||||||
|
grid.templateRows = { Track (Fr (5)), Track (Fr (12)), Track (Fr (8)),Track (Fr (20)), Track (Fr (4)) };
|
||||||
|
grid.templateColumns = { Track (Fr (1)) };
|
||||||
|
|
||||||
|
float margin = 1.0f;
|
||||||
|
grid.items = {
|
||||||
|
juce::GridItem (measures).withMargin(juce::GridItem::Margin(0.0f, 0.0f, margin, 0.0f)),
|
||||||
|
juce::GridItem (bpm).withMargin(juce::GridItem::Margin(margin, 0.0f, margin, 0.0f)),
|
||||||
|
juce::GridItem (tempo).withMargin(juce::GridItem::Margin(margin, 0.0f, margin, 0.0f)),
|
||||||
|
juce::GridItem(playButton).withMargin(juce::GridItem::Margin(margin, 0.0f, margin, 0.0f)),
|
||||||
|
juce::GridItem (options).withMargin(juce::GridItem::Margin(margin, 0.0f, 0.0f, 0.0f)),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
grid.performLayout (getLocalBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainComponent::timerCallback()
|
||||||
|
{
|
||||||
|
// On regarde si le metronome à des choses à nous dire (changement de mesure)
|
||||||
|
if (metronome.signal.get() == true)
|
||||||
|
{
|
||||||
|
int cur = metronome.getCurrentMeasure();
|
||||||
|
metronome.signal.set(false); // on ferme le signal
|
||||||
|
int meas = metronome.getMeasures();
|
||||||
|
cur = cur > 0 ? cur - 1 : cur;
|
||||||
|
measures.update(meas, cur % meas);
|
||||||
|
if (cur != lastcur)
|
||||||
|
{
|
||||||
|
// On à changé de mesure, on indique au playButton de s'allumer pendant 6 frames
|
||||||
|
playButton.highlightFrames = 6;
|
||||||
|
lastcur = cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playButton.highlight(); // le bouton va se redessiner et decrementer son highlightFrames
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainComponent::saveConfig()
|
||||||
|
{
|
||||||
|
// sauvegarde la configuration du metronome à la fermeture
|
||||||
|
Config::Save(metronome, options);
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "MesurePanel.h"
|
||||||
|
#include "BPMPanel.h"
|
||||||
|
#include "TempoPanel.h"
|
||||||
|
#include "PlayPanel.h"
|
||||||
|
#include "OptionsPanel.h"
|
||||||
|
#include "Metronome.h"
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
This component lives inside our window, and this is where you should put all
|
||||||
|
your controls and content.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MainComponent : private juce::Timer, public juce::AudioAppComponent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//==============================================================================
|
||||||
|
MainComponent(Config& conf);
|
||||||
|
~MainComponent();
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void prepareToPlay (int , double sampleRate) override;
|
||||||
|
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override;
|
||||||
|
void releaseResources() override;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void paint (juce::Graphics& g) override;
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
void saveConfig();
|
||||||
|
|
||||||
|
private:
|
||||||
|
MesurePanel measures;
|
||||||
|
BPMPanel bpm;
|
||||||
|
PlayPanel playButton;
|
||||||
|
TempoPanel tempo;
|
||||||
|
OptionsPanel options;
|
||||||
|
|
||||||
|
Metronome metronome;
|
||||||
|
|
||||||
|
// conserve la dernière mesure pour déclencher le highlight en cas de changement
|
||||||
|
int lastcur;
|
||||||
|
|
||||||
|
// Utilisation d'un timer pour relire les signaux du metronome
|
||||||
|
// timer de 20ms, largement suffisant pour un retour graphique sans surcharger le processeur (~50fps)
|
||||||
|
void timerCallback() override;
|
||||||
|
|
||||||
|
// permet de faire clignoter le bouton play sur le temps
|
||||||
|
void highlight();
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
#include "MesurePanel.h"
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion du nombre de temps par mesure.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void RoundCBLookAndFeel::drawToggleButton(juce::Graphics& g, juce::ToggleButton& button,
|
||||||
|
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown)
|
||||||
|
{
|
||||||
|
// dessine un cercle au lieux d'un bouton
|
||||||
|
auto fontSize = juce::jmin(15.0f, (float)button.getHeight() * 0.75f);
|
||||||
|
auto tickWidth = fontSize * 1.1f;
|
||||||
|
|
||||||
|
drawTickBox(g, button, 4.0f, ((float)button.getHeight() - tickWidth) * 0.5f,
|
||||||
|
tickWidth, tickWidth,
|
||||||
|
button.getToggleState(),
|
||||||
|
button.isEnabled(),
|
||||||
|
shouldDrawButtonAsHighlighted,
|
||||||
|
shouldDrawButtonAsDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoundCBLookAndFeel::drawTickBox(juce::Graphics& g, juce::Component& component,
|
||||||
|
float x, float y, float w, float h,
|
||||||
|
bool ticked, bool isEnabled,
|
||||||
|
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown)
|
||||||
|
{
|
||||||
|
// dessine un cercle au lieux d'un bouton
|
||||||
|
juce::ignoreUnused(isEnabled, shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown);
|
||||||
|
|
||||||
|
g.setColour(component.findColour(juce::ToggleButton::tickDisabledColourId));
|
||||||
|
g.drawEllipse(x, y, w, h, 1.0f);
|
||||||
|
|
||||||
|
if (ticked)
|
||||||
|
{
|
||||||
|
g.setColour(component.findColour(juce::ToggleButton::tickColourId));
|
||||||
|
g.fillEllipse(x, y, w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MesurePanel::MesurePanel(juce::Colour c) : backgroundColour(c)
|
||||||
|
{
|
||||||
|
lef.reset(new RoundCBLookAndFeel());
|
||||||
|
update(4, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
MesurePanel::~MesurePanel()
|
||||||
|
{
|
||||||
|
// evite une fuite mémoire à la fermeture due au L&F
|
||||||
|
for (auto& tb : buttons)
|
||||||
|
{
|
||||||
|
removeChildComponent(tb);
|
||||||
|
tb->setLookAndFeel(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MesurePanel::paint(juce::Graphics& g)
|
||||||
|
{
|
||||||
|
g.fillAll(backgroundColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MesurePanel::update(int nb, int cur)
|
||||||
|
{
|
||||||
|
// Construit autant de bouton qu'il y a de temps
|
||||||
|
if (buttons.size() != nb)
|
||||||
|
{
|
||||||
|
// Si la mesure à changé on reinitialise
|
||||||
|
for (auto& tb : buttons)
|
||||||
|
{
|
||||||
|
removeChildComponent(tb);
|
||||||
|
tb->setVisible(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.clear();
|
||||||
|
for (int i = 0; i < nb; ++i)
|
||||||
|
{
|
||||||
|
auto* tb = new juce::ToggleButton(juce::String(i));
|
||||||
|
addAndMakeVisible(buttons.add(tb));
|
||||||
|
tb->setRadioGroupId(1234);
|
||||||
|
tb->setColour(juce::ToggleButton::tickDisabledColourId, juce::Colours::black);
|
||||||
|
tb->setColour(juce::ToggleButton::tickColourId, juce::Colours::black);
|
||||||
|
tb->setLookAndFeel(lef.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On sélectionne le bouton correpondant au temps courant
|
||||||
|
for (int i = 0; i < nb; ++i)
|
||||||
|
{
|
||||||
|
buttons[i]->setToggleState(i == cur, juce::dontSendNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
resized();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MesurePanel::resized()
|
||||||
|
{
|
||||||
|
// Layout via une flexbox pour répartir les boutons en ligne
|
||||||
|
juce::FlexBox fb;
|
||||||
|
fb.flexWrap = juce::FlexBox::Wrap::wrap;
|
||||||
|
fb.justifyContent = juce::FlexBox::JustifyContent::center;
|
||||||
|
fb.alignContent = juce::FlexBox::AlignContent::center;
|
||||||
|
|
||||||
|
for (auto* b : buttons)
|
||||||
|
fb.items.add(juce::FlexItem(*b).withMinWidth(40.0f).withMinHeight(40.0f));
|
||||||
|
|
||||||
|
fb.performLayout(getLocalBounds().toFloat());
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion du nombre de temps par mesure.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
|
||||||
|
// Surcharge les boutons pour un rond plein ou vide
|
||||||
|
struct RoundCBLookAndFeel : public juce::LookAndFeel_V4
|
||||||
|
{
|
||||||
|
void drawToggleButton(juce::Graphics& g, juce::ToggleButton& button,
|
||||||
|
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override;
|
||||||
|
|
||||||
|
void drawTickBox(juce::Graphics& g, juce::Component& component,
|
||||||
|
float x, float y, float w, float h,
|
||||||
|
bool ticked, bool isEnabled,
|
||||||
|
bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Panneau d'affichage de la mesure en cours
|
||||||
|
struct MesurePanel : public juce::Component
|
||||||
|
{
|
||||||
|
explicit MesurePanel(juce::Colour c);
|
||||||
|
|
||||||
|
~MesurePanel();
|
||||||
|
|
||||||
|
// Méthode de surcharge JUCE
|
||||||
|
void paint(juce::Graphics& g) override;
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
// méthodes internes
|
||||||
|
void update(int nb, int cur);
|
||||||
|
|
||||||
|
// composants graphiques
|
||||||
|
juce::Colour backgroundColour;
|
||||||
|
juce::OwnedArray<juce::ToggleButton> buttons;
|
||||||
|
std::unique_ptr <RoundCBLookAndFeel> lef;
|
||||||
|
};
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Moteur de l'application. Gère la lecture des samples Callé sur le bon tempo.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Metronome.h"
|
||||||
|
|
||||||
|
Metronome::Metronome(Config& conf)
|
||||||
|
{
|
||||||
|
// on récupère les valeurs de la configuration
|
||||||
|
bpm.set(conf.bpm);
|
||||||
|
measures.set(conf.measures);
|
||||||
|
gain.set(conf.gain);
|
||||||
|
setFigure(conf.figure);
|
||||||
|
|
||||||
|
// on initialise le lecteur wav
|
||||||
|
formatManager.registerBasicFormats();
|
||||||
|
|
||||||
|
// On crée 2 voie sur le synthé
|
||||||
|
for (auto i = 0; i < 2; ++i)
|
||||||
|
synth.addVoice(new juce::SamplerVoice());
|
||||||
|
}
|
||||||
|
|
||||||
|
Metronome::~Metronome()
|
||||||
|
{
|
||||||
|
// libère les sons
|
||||||
|
synth.clearSounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Metronome::loadSounds(std::string high, std::string low)
|
||||||
|
{
|
||||||
|
// chargement des sons high et low
|
||||||
|
auto mySamplesHigh = juce::File{high};
|
||||||
|
auto mySamplesLow = juce::File{ low };
|
||||||
|
|
||||||
|
jassert(mySamplesHigh.exists());
|
||||||
|
jassert(mySamplesLow.exists());
|
||||||
|
synth.clearSounds();
|
||||||
|
|
||||||
|
juce::BigInteger usedNotes;
|
||||||
|
usedNotes.setRange(0, 1, true);
|
||||||
|
auto formatReaderHigh = formatManager.createReaderFor(mySamplesHigh);
|
||||||
|
synth.addSound(new juce::SamplerSound("High", *formatReaderHigh, usedNotes, 0, 0.0f, 0.0f, 999.0));
|
||||||
|
delete formatReaderHigh;
|
||||||
|
|
||||||
|
juce::BigInteger usedNotes2;
|
||||||
|
usedNotes2.setRange(1, 1, true);
|
||||||
|
auto formatReaderLow = formatManager.createReaderFor(mySamplesLow);
|
||||||
|
synth.addSound(new juce::SamplerSound("Low", *formatReaderLow, usedNotes2, 1, 0.0f, 0.0f, 999.0));
|
||||||
|
delete formatReaderLow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Metronome::prepareToPlay (double sr)
|
||||||
|
{
|
||||||
|
// Appelé avant de démaré la lecture, on récupère la sr et on initialise le reste du moteur
|
||||||
|
sampleRate = sr;
|
||||||
|
updateInterval = (int)(60.0 / bpm.get() * sampleRate);
|
||||||
|
synth.setCurrentPlaybackSampleRate(sampleRate);
|
||||||
|
initMeasure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Metronome::getNextAudioBlock(juce::AudioSourceChannelInfo const& bufferToFill)
|
||||||
|
{
|
||||||
|
// Méthode invoqué par le moteur audio. Le but est de remplir bufferToFill avec la waveform que l'on souhaite jouer
|
||||||
|
int const numSamples = bufferToFill.numSamples;
|
||||||
|
|
||||||
|
juce::MidiBuffer midi;
|
||||||
|
int measCount = measures.get();
|
||||||
|
|
||||||
|
int measLength = updateInterval * measCount; // nombre de sample total dans une mesure
|
||||||
|
int start = lastPos; // Le premier index du buffer correspond au lastPos
|
||||||
|
int stop = lastPos + numSamples; // et la dernière position du buffer
|
||||||
|
for (auto cur : measStruct)
|
||||||
|
{
|
||||||
|
// On cherche Si dans notre mesure on a un click entre start et stop
|
||||||
|
int nextTick = (int)(cur.measPos * updateInterval); // le measPos est en relatif, on convertit en équivalent samples
|
||||||
|
if (stop > measLength)
|
||||||
|
{
|
||||||
|
// Dans le cas ou stop se situe sur la mesure suivante, on décale le nextTick d'une mesure
|
||||||
|
nextTick += measLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextTick >= start && nextTick < stop)
|
||||||
|
{
|
||||||
|
// On à trouvé un tick
|
||||||
|
if (cur.type != NoteType::mute)
|
||||||
|
{
|
||||||
|
// qui est pas mute
|
||||||
|
// on sélectionne la bone note, la bonne vélocité et on génère un événement midi à nextTick - start
|
||||||
|
int note = cur.type == NoteType::high ? 0 : 1;
|
||||||
|
float vel = cur.type == NoteType::lowLow ? 0.2f : 1.0f;
|
||||||
|
midi.addEvent(juce::MidiMessage::noteOn(1, note, vel), nextTick - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
// on signale l'interface (pour marquer le temps même s'il est mute)
|
||||||
|
signal.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On incrémente lastPos
|
||||||
|
lastPos = stop;
|
||||||
|
if (lastPos > measLength)
|
||||||
|
{
|
||||||
|
// Et on le remet dans la mesure suivante
|
||||||
|
lastPos -= measLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On demande au synthé de remplir le buffer
|
||||||
|
synth.renderNextBlock(*bufferToFill.buffer,
|
||||||
|
midi,
|
||||||
|
0,
|
||||||
|
bufferToFill.numSamples);
|
||||||
|
|
||||||
|
// On reprend la main sur le buffer pour appliquer le gain manuellement
|
||||||
|
float mult = gain.get();
|
||||||
|
for (auto channel = 0; channel < bufferToFill.buffer->getNumChannels(); ++channel)
|
||||||
|
{
|
||||||
|
auto* buffer = bufferToFill.buffer->getWritePointer(channel, bufferToFill.startSample);
|
||||||
|
|
||||||
|
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
|
||||||
|
buffer[sample] = buffer[sample] * mult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Metronome::reset()
|
||||||
|
{
|
||||||
|
// raz
|
||||||
|
lastPos = 0;
|
||||||
|
signal.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Metronome::setBPM(int value)
|
||||||
|
{
|
||||||
|
// contraint les BPM entre 10 et 400
|
||||||
|
int coerce = juce::jlimit(10, 400, value);
|
||||||
|
bpm.set(coerce);
|
||||||
|
|
||||||
|
int lastInterval = updateInterval;
|
||||||
|
updateInterval = (int)(60.0 / bpm.get() * sampleRate);
|
||||||
|
// En cas de changement de BPM brutal on constate un décalage dans la lecture
|
||||||
|
// Pour remédier à ca on recalibre le lastPos en proportion du nouvel intervale
|
||||||
|
lastPos = (int)(lastPos * (float)updateInterval / (float)lastInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Metronome::getCurrentMeasure() const
|
||||||
|
{
|
||||||
|
// permet à l'UI de savoir sur quel temps de la mesure on se trouve
|
||||||
|
int numTemps = (lastPos + updateInterval - 1) / updateInterval; // nombre de temps depuis le start
|
||||||
|
return numTemps;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Metronome::initMeasure()
|
||||||
|
{
|
||||||
|
// construit la structure de la mesure
|
||||||
|
measStruct.clear();
|
||||||
|
int measCount = measures.get();
|
||||||
|
switch (figure.get())
|
||||||
|
{
|
||||||
|
case FigureType::blanche:
|
||||||
|
// On alterne click/Mute
|
||||||
|
for (size_t i = 0; i < measCount; ++i)
|
||||||
|
{
|
||||||
|
if (i % 2 == 0)
|
||||||
|
{
|
||||||
|
measStruct.push_back(MeasureTick((float)i, i == 0 ? NoteType::high : NoteType::low));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
measStruct.push_back(MeasureTick((float)i, NoteType::mute));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case FigureType::noire:
|
||||||
|
// Un click par temps
|
||||||
|
for (size_t i = 0; i < measCount; ++i)
|
||||||
|
{
|
||||||
|
measStruct.push_back(MeasureTick((float)i, i == 0 ? NoteType::high : NoteType::low));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case FigureType::croche:
|
||||||
|
// On alterne click/lowlow
|
||||||
|
for (size_t i = 0; i < measCount; ++i)
|
||||||
|
{
|
||||||
|
measStruct.push_back(MeasureTick((float)i, i == 0 ? NoteType::high : NoteType::low));
|
||||||
|
measStruct.push_back(MeasureTick((float)i + 0.5f, NoteType::lowLow));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case FigureType::triolet:
|
||||||
|
// On alterne click/lowlow/lowlow
|
||||||
|
for (size_t i = 0; i < measCount; ++i)
|
||||||
|
{
|
||||||
|
measStruct.push_back(MeasureTick((float)i, i == 0 ? NoteType::high : NoteType::low));
|
||||||
|
measStruct.push_back(MeasureTick((float)i + 1.f / 3.f, NoteType::lowLow));
|
||||||
|
measStruct.push_back(MeasureTick((float)i + 2.f / 3.f, NoteType::lowLow));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FigureType::doubleCroche:
|
||||||
|
// On alterne click/lowlow/lowlow/lowlow
|
||||||
|
for (size_t i = 0; i < measCount; ++i)
|
||||||
|
{
|
||||||
|
measStruct.push_back(MeasureTick((float)i, i == 0 ? NoteType::high : NoteType::low));
|
||||||
|
measStruct.push_back(MeasureTick((float)i + 0.25f, NoteType::lowLow));
|
||||||
|
measStruct.push_back(MeasureTick((float)i + 0.5f, NoteType::lowLow));
|
||||||
|
measStruct.push_back(MeasureTick((float)i + 0.75f, NoteType::lowLow));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FigureType::swing:
|
||||||
|
// On alterne click/lowlow sur les 2/3 du temps
|
||||||
|
for (size_t i = 0; i < measCount; ++i)
|
||||||
|
{
|
||||||
|
measStruct.push_back(MeasureTick((float)i, i == 0 ? NoteType::high : NoteType::low));
|
||||||
|
measStruct.push_back(MeasureTick((float)i + 2.f / 3.f, NoteType::lowLow));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Moteur de l'application. Gère la lecture des samples Callé sur le bon tempo.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
class Config;
|
||||||
|
|
||||||
|
// Défini les 3 niveaux de clic utilisés par le métronome
|
||||||
|
// high marque le premier temps de la mesure. correspond au sample High*.wav
|
||||||
|
// low marque les autres temps de la mesure. correspond au sample Low*.wav
|
||||||
|
// lowlow est utilisé pour les clics intermédiares (croches,...). correspond au sample Low*.wav avec une vélocité réduite
|
||||||
|
// mute permet de marquer un silence sur le temps lorsqu'on bat la mesure à la blanche
|
||||||
|
enum class NoteType { high = 0, low = 1, lowLow = 2, mute = 3 };
|
||||||
|
|
||||||
|
// Les figures rythmiques disponibles : blanche, noire, croche, triolet, double croche
|
||||||
|
// la figure swing (ou shuffle) est construite sur un triolet avec un click sur le temps et un sur le 2/3 de temps.
|
||||||
|
// suivant l'interprétation, le swing devrait su situer entre le triolet et la double croche mais c'est moi qui décide.
|
||||||
|
enum class FigureType { blanche = 0, noire = 1, croche = 2, triolet = 3, doubleCroche = 4, swing = 5 };
|
||||||
|
|
||||||
|
// structure de base pour définir la structure rythmique de la mesure
|
||||||
|
struct MeasureTick
|
||||||
|
{
|
||||||
|
// position du click dans la mesure en valeur absolu
|
||||||
|
// pour une mesure de 5 temps measPos est dans l'intervale [0, 5[
|
||||||
|
float measPos;
|
||||||
|
|
||||||
|
// type de click
|
||||||
|
NoteType type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bat la mesure
|
||||||
|
class Metronome
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Metronome(Config& configuration);
|
||||||
|
~Metronome();
|
||||||
|
|
||||||
|
juce::Atomic<bool> signal;
|
||||||
|
|
||||||
|
// méthodes de gestion du lecteur
|
||||||
|
void prepareToPlay (double sampleRate);
|
||||||
|
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill);
|
||||||
|
|
||||||
|
// reinitialise le lecteur sur le stop
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// recharge les son high et low
|
||||||
|
void loadSounds(std::string high, std::string low);
|
||||||
|
|
||||||
|
// accesseurs
|
||||||
|
int getBPM() const noexcept
|
||||||
|
{
|
||||||
|
return bpm.get();
|
||||||
|
}
|
||||||
|
void setBPM(int value);
|
||||||
|
|
||||||
|
int getMeasures() const noexcept
|
||||||
|
{
|
||||||
|
return measures.get();
|
||||||
|
}
|
||||||
|
void setMeasures(int value) {
|
||||||
|
int coerce = juce::jlimit(1, 9, value); // On limite le nombre de mesure à 9
|
||||||
|
measures.set(coerce);
|
||||||
|
initMeasure(); // reconstruit la structure de la mesure
|
||||||
|
signal.set(true); // signale à la GUI de se metre à jours
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCurrentMeasure() const;
|
||||||
|
|
||||||
|
void setGain(float value) {
|
||||||
|
gain.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getGain() {
|
||||||
|
return gain.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFigure(int value) {
|
||||||
|
figure.set((FigureType)value);
|
||||||
|
initMeasure(); // reconstruit la structure de la mesure
|
||||||
|
}
|
||||||
|
|
||||||
|
FigureType getFigure() {
|
||||||
|
return figure.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
// variables réglables depuis l'UI (d'ou le Atomic)
|
||||||
|
juce::Atomic<int> measures{ 4 }; // nombre de temps par mesure
|
||||||
|
juce::Atomic<int> bpm{ 72 }; // tempo
|
||||||
|
juce::Atomic<float> gain{ 1.0f }; // volume sonore
|
||||||
|
juce::Atomic<FigureType> figure{ FigureType::noire }; // type de figure utilisé pour construire la mesure
|
||||||
|
|
||||||
|
// variables internes
|
||||||
|
double sampleRate { 0 }; // nombre de sample par secondes
|
||||||
|
int updateInterval{ 0 }; // nombre de sample entre 2 temps : 60/bpm*sr
|
||||||
|
int lastPos{ 0 }; // indice du dernier sample traité lors de l'appel getNextAudioBlock. Valeur réinitialisé à chaque mesure modulo la taille de la mesure
|
||||||
|
std::vector<MeasureTick> measStruct; // structure de la mesure, contient tout les clicks en fonction du nombre de mesure et de la figure courante
|
||||||
|
|
||||||
|
juce::AudioFormatManager formatManager; // chargement des fichier wav
|
||||||
|
juce::Synthesiser synth; // la gestion direct du remplissage du buffer via getNextAudioBlock est trop compliqué
|
||||||
|
// On passe par un Synthé avec un fichier par note, on déclenche la lecture par un evenement midi déclenché précisément
|
||||||
|
|
||||||
|
|
||||||
|
void initMeasure(); // reconstruit la structure de la mesure
|
||||||
|
};
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion des options figure rythmique et fichier son du tick.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "OptionsPanel.h"
|
||||||
|
#include "Figures.h"
|
||||||
|
|
||||||
|
void ClickSounds::init()
|
||||||
|
{
|
||||||
|
// on récupère tout les sous dossiers dans App/Clicks
|
||||||
|
juce::File curDir{ juce::File::getSpecialLocation(juce::File::SpecialLocationType::currentExecutableFile).getParentDirectory() };
|
||||||
|
juce::File clickDir{ curDir.getChildFile("Clicks")};
|
||||||
|
auto it = clickDir.findChildFiles(juce::File::findDirectories, false);
|
||||||
|
for (int i = 0; i < it.size(); ++i)
|
||||||
|
{
|
||||||
|
juce::File sub = it[i];
|
||||||
|
bool highFile = !sub.findChildFiles(juce::File::TypesOfFileToFind::findFiles, false, "High*.wav").isEmpty();
|
||||||
|
bool lowFile = !sub.findChildFiles(juce::File::TypesOfFileToFind::findFiles, false, "Low*.wav").isEmpty();
|
||||||
|
if (highFile && lowFile)
|
||||||
|
{
|
||||||
|
// On n'ajoute que si on a bien les fichiers High et Low
|
||||||
|
juce::String path = sub.getFullPathName();
|
||||||
|
std::string ss = path.toStdString();
|
||||||
|
sounds.push_back(ss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ClickSounds::getSoundName(int i) const
|
||||||
|
{
|
||||||
|
// Valeur affiché dans la combo, on récupère le nom du sous dossier
|
||||||
|
juce::File path{ sounds[i] };
|
||||||
|
return path.getFileNameWithoutExtension().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ClickSounds::getSoundPathHigh(int i) const
|
||||||
|
{
|
||||||
|
// Chemin vers le fichier High
|
||||||
|
juce::File path{ sounds[i] };
|
||||||
|
return path.findChildFiles(juce::File::TypesOfFileToFind::findFiles, false, "High*.wav")[0].getFullPathName().toStdString();
|
||||||
|
}
|
||||||
|
std::string ClickSounds::getSoundPathLow(int i) const
|
||||||
|
{
|
||||||
|
// Chemin vers le fichier Low
|
||||||
|
juce::File path{ sounds[i] };
|
||||||
|
return path.findChildFiles(juce::File::TypesOfFileToFind::findFiles, false, "Low*.wav")[0].getFullPathName().toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClickSounds::getIndex(std::string soundName) const
|
||||||
|
{
|
||||||
|
// récupère l'index du fichier à partir de son nom
|
||||||
|
// Utilisé pour restaurer la configuration
|
||||||
|
// Du coup si le son n'est pas trouvé on retourne le premier son
|
||||||
|
for (int i = 0; i < sounds.size(); ++i)
|
||||||
|
{
|
||||||
|
if (getSoundName(i) == soundName)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComboLookAndFeel::drawPopupMenuBackground(juce::Graphics& g, int width, int height)
|
||||||
|
{
|
||||||
|
// Permet de setter le background du PopupMenu
|
||||||
|
g.fillAll(juce::Colours::whitesmoke);
|
||||||
|
juce::ignoreUnused(width, height);
|
||||||
|
|
||||||
|
#if ! JUCE_MAC
|
||||||
|
g.setColour(juce::Colours::darkgrey.withAlpha(0.6f));
|
||||||
|
g.drawRect(0, 0, width, height);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComboLookAndFeel::drawPopupMenuItem(juce::Graphics& g, const juce::Rectangle<int>& area,
|
||||||
|
const bool isSeparator, const bool isActive,
|
||||||
|
const bool isHighlighted, const bool isTicked,
|
||||||
|
const bool hasSubMenu, const juce::String& text,
|
||||||
|
const juce::String& shortcutKeyText,
|
||||||
|
const juce::Drawable* icon, const juce::Colour* const textColourToUse)
|
||||||
|
{
|
||||||
|
// Permet de setter le background du PopupMenuItem
|
||||||
|
if (isSeparator)
|
||||||
|
{
|
||||||
|
auto r = area.reduced(5, 0);
|
||||||
|
r.removeFromTop(juce::roundToInt(((float)r.getHeight() * 0.5f) - 0.5f));
|
||||||
|
|
||||||
|
g.setColour(juce::Colours::darkgrey.withAlpha(0.3f));
|
||||||
|
g.fillRect(r.removeFromTop(1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto textColour = (textColourToUse == nullptr ? juce::Colours::darkgrey
|
||||||
|
: *textColourToUse);
|
||||||
|
|
||||||
|
auto r = area.reduced(1);
|
||||||
|
|
||||||
|
if (isHighlighted && isActive)
|
||||||
|
{
|
||||||
|
g.setColour(juce::Colours::lightgrey);
|
||||||
|
g.fillRect(r);
|
||||||
|
|
||||||
|
g.setColour(juce::Colours::darkgrey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g.setColour(textColour.withMultipliedAlpha(isActive ? 1.0f : 0.5f));
|
||||||
|
}
|
||||||
|
|
||||||
|
r.reduce(juce::jmin(5, area.getWidth() / 20), 0);
|
||||||
|
|
||||||
|
auto font = getPopupMenuFont();
|
||||||
|
|
||||||
|
auto maxFontHeight = (float)r.getHeight() / 1.3f;
|
||||||
|
|
||||||
|
if (font.getHeight() > maxFontHeight)
|
||||||
|
font.setHeight(maxFontHeight);
|
||||||
|
|
||||||
|
g.setFont(font);
|
||||||
|
|
||||||
|
auto iconArea = r.removeFromLeft(juce::roundToInt(maxFontHeight)).toFloat();
|
||||||
|
|
||||||
|
if (icon != nullptr)
|
||||||
|
{
|
||||||
|
icon->drawWithin(g, iconArea, juce::RectanglePlacement::centred | juce::RectanglePlacement::onlyReduceInSize, 1.0f);
|
||||||
|
r.removeFromLeft(juce::roundToInt(maxFontHeight * 0.5f));
|
||||||
|
}
|
||||||
|
else if (isTicked)
|
||||||
|
{
|
||||||
|
auto tick = getTickShape(1.0f);
|
||||||
|
g.fillPath(tick, tick.getTransformToScaleToFit(iconArea.reduced(iconArea.getWidth() / 5, 0).toFloat(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasSubMenu)
|
||||||
|
{
|
||||||
|
auto arrowH = 0.6f * getPopupMenuFont().getAscent();
|
||||||
|
|
||||||
|
auto x = static_cast<float> (r.removeFromRight((int)arrowH).getX());
|
||||||
|
auto halfH = static_cast<float> (r.getCentreY());
|
||||||
|
|
||||||
|
juce::Path path;
|
||||||
|
path.startNewSubPath(x, halfH - arrowH * 0.5f);
|
||||||
|
path.lineTo(x + arrowH * 0.6f, halfH);
|
||||||
|
path.lineTo(x, halfH + arrowH * 0.5f);
|
||||||
|
|
||||||
|
g.strokePath(path, juce::PathStrokeType(2.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
r.removeFromRight(3);
|
||||||
|
g.drawFittedText(text, r, juce::Justification::centredLeft, 1);
|
||||||
|
|
||||||
|
if (shortcutKeyText.isNotEmpty())
|
||||||
|
{
|
||||||
|
auto f2 = font;
|
||||||
|
f2.setHeight(f2.getHeight() * 0.75f);
|
||||||
|
f2.setHorizontalScale(0.95f);
|
||||||
|
g.setFont(f2);
|
||||||
|
|
||||||
|
g.drawText(shortcutKeyText, r, juce::Justification::centredRight, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OptionsPanel::OptionsPanel(juce::Colour c, Config& configuration) : backgroundColour(c)
|
||||||
|
{
|
||||||
|
// initialisation de l'interface
|
||||||
|
clef.reset(new ComboLookAndFeel());
|
||||||
|
addAndMakeVisible(figures);
|
||||||
|
auto doc = juce::XmlDocument::parse(SVGFigures::noire); // on charge la noire par defaut, va être re-initialisé par la configuration
|
||||||
|
auto note = juce::Drawable::createFromSVG(*doc);
|
||||||
|
figures.setImages(note.get());
|
||||||
|
figures.setToggleState(false, juce::NotificationType::dontSendNotification);
|
||||||
|
figures.setColour(juce::TextButton::buttonColourId, juce::Colours::whitesmoke);
|
||||||
|
figures.setColour(juce::TextButton::textColourOffId, juce::Colours::black);
|
||||||
|
figures.onClick = [&]
|
||||||
|
{
|
||||||
|
// génère le dropdown menu des figures
|
||||||
|
juce::PopupMenu menu;
|
||||||
|
|
||||||
|
int width = 70;
|
||||||
|
int height = 48;
|
||||||
|
menu.addCustomItem(1, std::make_unique<FigureButton>(SVGFigures::blanche, width, height, juce::Colours::whitesmoke));
|
||||||
|
menu.addCustomItem(4, std::make_unique<FigureButton>(SVGFigures::triolet, width, height, juce::Colours::whitesmoke));
|
||||||
|
menu.addCustomItem(2, std::make_unique<FigureButton>(SVGFigures::noire, width, height, juce::Colours::whitesmoke));
|
||||||
|
menu.addCustomItem(5, std::make_unique<FigureButton>(SVGFigures::doubles, width, height, juce::Colours::whitesmoke));
|
||||||
|
menu.addCustomItem(3, std::make_unique<FigureButton>(SVGFigures::croches, width, height, juce::Colours::whitesmoke));
|
||||||
|
menu.addCustomItem(6, std::make_unique<FigureButton>(SVGFigures::swing, width, height, juce::Colours::whitesmoke));
|
||||||
|
|
||||||
|
menu.showMenuAsync(juce::PopupMenu::Options{}.withTargetComponent(figures)
|
||||||
|
.withMinimumNumColumns(3), [&](int resultId) { if(resultId > 0) setFigure(resultId - 1); });
|
||||||
|
};
|
||||||
|
|
||||||
|
addAndMakeVisible(sounds);
|
||||||
|
sounds.setLookAndFeel(clef.get());
|
||||||
|
sounds.setColour(juce::ComboBox::backgroundColourId, juce::Colours::whitesmoke);
|
||||||
|
sounds.setColour(juce::ComboBox::outlineColourId, juce::Colours::darkgrey);
|
||||||
|
sounds.setColour(juce::ComboBox::arrowColourId, juce::Colours::darkgrey);
|
||||||
|
sounds.setColour(juce::ComboBox::textColourId, juce::Colours::darkgrey);
|
||||||
|
clicksounds.init(); // charge la liste des sons
|
||||||
|
curSoundID = clicksounds.getIndex(configuration.sound.toStdString()); // récupère le son courant dans la configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
juce::String OptionsPanel::getSoundName()
|
||||||
|
{
|
||||||
|
// Récupère le nom du son courant pour sauver la config
|
||||||
|
return clicksounds.getSoundName(sounds.getSelectedId() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionsPanel::setFigure(int figure)
|
||||||
|
{
|
||||||
|
// MàJ de l'icone de la figure courante
|
||||||
|
juce::String newSVG;
|
||||||
|
switch (figure)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
newSVG = SVGFigures::blanche;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
newSVG = SVGFigures::croches;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
newSVG = SVGFigures::triolet;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
newSVG = SVGFigures::doubles;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
newSVG = SVGFigures::swing;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
default:
|
||||||
|
newSVG = SVGFigures::noire;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto doc = juce::XmlDocument::parse(newSVG);
|
||||||
|
auto note = juce::Drawable::createFromSVG(*doc);
|
||||||
|
figures.setImages(note.get());
|
||||||
|
setMetroFigure(figure);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsPanel::~OptionsPanel()
|
||||||
|
{
|
||||||
|
// evite une fuite mémoire à la fermeture due au L&F
|
||||||
|
sounds.setLookAndFeel(nullptr);
|
||||||
|
figures.setLookAndFeel(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionsPanel::paint(juce::Graphics& g)
|
||||||
|
{
|
||||||
|
g.fillAll(backgroundColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionsPanel::resized()
|
||||||
|
{
|
||||||
|
// On utilise un layout grille en 2 colonnes
|
||||||
|
int margin = 3;
|
||||||
|
juce::Grid grid;
|
||||||
|
using Track = juce::Grid::TrackInfo;
|
||||||
|
using Fr = juce::Grid::Fr;
|
||||||
|
grid.templateRows = { Track(Fr(1)) };
|
||||||
|
grid.templateColumns = { Track(Fr(1)), Track(Fr(1)) };
|
||||||
|
grid.items = {
|
||||||
|
juce::GridItem(figures).withMargin(juce::GridItem::Margin(margin)),
|
||||||
|
juce::GridItem(sounds).withMargin(juce::GridItem::Margin(margin)),
|
||||||
|
};
|
||||||
|
|
||||||
|
grid.performLayout(getLocalBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionsPanel::init(Metronome& metro)
|
||||||
|
{
|
||||||
|
// On établi les liaisons entre le Metronome et les composants graphique
|
||||||
|
for (int i = 0; i < clicksounds.getSoundCount(); ++i)
|
||||||
|
{
|
||||||
|
sounds.addItem(clicksounds.getSoundName(i), i + 1); // La combo n'autorise pas 0 comme Id, d'ou le + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sounds.setSelectedId(curSoundID + 1); // toujours ce + 1
|
||||||
|
selectSound(metro); // charge le son courant dans le metronome
|
||||||
|
sounds.onChange = [&, this]() { selectSound(metro); };
|
||||||
|
setMetroFigure = [&](int figure) { metro.setFigure(figure); };
|
||||||
|
setFigure((int)metro.getFigure());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionsPanel::selectSound(Metronome& metro)
|
||||||
|
{
|
||||||
|
// charge le son courant dans le metronome
|
||||||
|
std::string high = clicksounds.getSoundPathHigh(sounds.getSelectedId() - 1);
|
||||||
|
std::string low = clicksounds.getSoundPathLow(sounds.getSelectedId() - 1);
|
||||||
|
metro.loadSounds(high, low);
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion des options figure rythmique et fichier son du tick.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "Metronome.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
class Config;
|
||||||
|
class Metronome;
|
||||||
|
|
||||||
|
// Surcharge les boutons pour afficher l'image de la figure rythmique
|
||||||
|
struct FigureButton : public juce::PopupMenu::CustomComponent
|
||||||
|
{
|
||||||
|
FigureButton(juce::String image, int widthIn, int heightIn, juce::Colour backgroundIn)
|
||||||
|
: juce::PopupMenu::CustomComponent(false),
|
||||||
|
idealWidth(widthIn),
|
||||||
|
idealHeight(heightIn),
|
||||||
|
background(backgroundIn)
|
||||||
|
{
|
||||||
|
// initialisation de l'interface
|
||||||
|
addAndMakeVisible(button);
|
||||||
|
// récupère l'image de la figure
|
||||||
|
auto doc = juce::XmlDocument::parse(image);
|
||||||
|
auto note = juce::Drawable::createFromSVG(*doc);
|
||||||
|
button.setImages(note.get());
|
||||||
|
button.setToggleState(false, juce::NotificationType::dontSendNotification);
|
||||||
|
button.setColour(juce::TextButton::buttonColourId, juce::Colours::whitesmoke);
|
||||||
|
button.setColour(juce::TextButton::textColourOffId, juce::Colours::black);
|
||||||
|
button.onClick = [&, this]
|
||||||
|
{
|
||||||
|
// notifie la "Combo"
|
||||||
|
this->triggerMenuItem();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void getIdealSize(int& width, int& height) override
|
||||||
|
{
|
||||||
|
width = idealWidth;
|
||||||
|
height = idealHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(juce::Graphics& g) override { g.fillAll(background); }
|
||||||
|
|
||||||
|
void resized() override { button.setBounds(getBounds()); }
|
||||||
|
|
||||||
|
// composants graphiques
|
||||||
|
juce::Colour background;
|
||||||
|
juce::DrawableButton button{ "Figures", juce::DrawableButton::ImageOnButtonBackground };
|
||||||
|
|
||||||
|
// taille du bouton
|
||||||
|
int idealWidth = 0;
|
||||||
|
int idealHeight = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// L&F pour la combo des sons, permet de setter le background du Popup
|
||||||
|
struct ComboLookAndFeel : public juce::LookAndFeel_V4
|
||||||
|
{
|
||||||
|
void drawPopupMenuBackground(juce::Graphics& g, int width, int height) override;
|
||||||
|
void drawPopupMenuItem(juce::Graphics& g, const juce::Rectangle<int>& area,
|
||||||
|
const bool isSeparator, const bool isActive,
|
||||||
|
const bool isHighlighted, const bool isTicked,
|
||||||
|
const bool hasSubMenu, const juce::String& text,
|
||||||
|
const juce::String& shortcutKeyText,
|
||||||
|
const juce::Drawable* icon, const juce::Colour* const textColourToUse) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// structure dédiée à la gestion des sons
|
||||||
|
struct ClickSounds
|
||||||
|
{
|
||||||
|
ClickSounds() {};
|
||||||
|
|
||||||
|
// Charge la liste de sons
|
||||||
|
void init();
|
||||||
|
|
||||||
|
// Accesseurs
|
||||||
|
int getSoundCount() const { return (int)sounds.size(); };
|
||||||
|
std::string getSoundName(int i) const;
|
||||||
|
std::string getSoundPathHigh(int i) const;
|
||||||
|
std::string getSoundPathLow(int i) const;
|
||||||
|
|
||||||
|
int getIndex(std::string soundName) const;
|
||||||
|
|
||||||
|
// Liste des sons
|
||||||
|
std::vector<std::string> sounds;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Panneau d'affichage des options
|
||||||
|
struct OptionsPanel : public juce::Component
|
||||||
|
{
|
||||||
|
explicit OptionsPanel(juce::Colour c, Config& configuration);
|
||||||
|
~OptionsPanel();
|
||||||
|
|
||||||
|
// Méthode de surcharge JUCE
|
||||||
|
void paint(juce::Graphics& g) override;
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
// méthodes internes
|
||||||
|
void init(Metronome& metro);
|
||||||
|
|
||||||
|
void selectSound(Metronome& metro);
|
||||||
|
void setFigure(int figure);
|
||||||
|
juce::String getSoundName();
|
||||||
|
|
||||||
|
// composants graphiques
|
||||||
|
juce::DrawableButton figures{ "Figures", juce::DrawableButton::ImageOnButtonBackground };
|
||||||
|
juce::ComboBox sounds;
|
||||||
|
juce::Colour backgroundColour;
|
||||||
|
std::unique_ptr <ComboLookAndFeel> clef;
|
||||||
|
|
||||||
|
// variables internes
|
||||||
|
// liste des sons
|
||||||
|
ClickSounds clicksounds;
|
||||||
|
|
||||||
|
// pointeur sur la méthode qui permet de setter le metronome
|
||||||
|
std::function< void(int)> setMetroFigure {nullptr};
|
||||||
|
|
||||||
|
// permet de stocker l'id du son selectionné dans la configuration entre le constructeur et le init()
|
||||||
|
int curSoundID;
|
||||||
|
};
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion du bouton play et du volume.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PlayPanel.h"
|
||||||
|
#include "Metronome.h"
|
||||||
|
#include "Figures.h"
|
||||||
|
|
||||||
|
void RoundButtonLookAndFeel::drawButtonBackground(juce::Graphics& g, juce::Button& button, const juce::Colour& backgroundColour,
|
||||||
|
bool isMouseOverButton, bool isButtonDown)
|
||||||
|
{
|
||||||
|
// version addapté du LookAndFeel_V4
|
||||||
|
auto baseColour = backgroundColour.withMultipliedSaturation(button.hasKeyboardFocus(true) ? 1.3f : 0.9f)
|
||||||
|
.withMultipliedAlpha(button.isEnabled() ? 0.9f : 0.5f);
|
||||||
|
|
||||||
|
if (isButtonDown || isMouseOverButton)
|
||||||
|
baseColour = baseColour.contrasting(isButtonDown ? 0.2f : 0.1f);
|
||||||
|
|
||||||
|
auto width = (float)button.getWidth() - 1.0f;
|
||||||
|
auto height = (float)button.getHeight() - 1.0f;
|
||||||
|
|
||||||
|
if (width > 0 && height > 0)
|
||||||
|
{
|
||||||
|
// on viens dessiner un cercle
|
||||||
|
auto rect = button.getLocalBounds();
|
||||||
|
float rwidth = rect.getWidth() < rect.getHeight() ? (float)rect.getWidth() - 25.0f : (float)rect.getHeight() - 25.0f;
|
||||||
|
|
||||||
|
g.setGradientFill(juce::ColourGradient::vertical(juce::Colours::lightgrey, 0.0f,
|
||||||
|
juce::Colours::lightgrey.darker(0.1f), height));
|
||||||
|
g.fillEllipse(rect.getCentreX() - rwidth / 2, rect.getCentreY() - rwidth / 2, rwidth, rwidth);
|
||||||
|
|
||||||
|
g.setGradientFill(juce::ColourGradient::vertical(baseColour, 0.0f,
|
||||||
|
baseColour.darker(0.1f), height));
|
||||||
|
|
||||||
|
g.drawEllipse(rect.getCentreX() - rwidth / 2, rect.getCentreY() - rwidth / 2, rwidth, rwidth, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayPanel::PlayPanel(juce::Colour c) : backgroundColour(c)
|
||||||
|
{
|
||||||
|
// initialisation de l'interface
|
||||||
|
lef.reset(new RoundButtonLookAndFeel());
|
||||||
|
|
||||||
|
// Le dessin du bouton play est dans un svg
|
||||||
|
auto doc = juce::XmlDocument::parse(SVGFigures::play);
|
||||||
|
auto image = juce::Drawable::createFromSVG(*doc);
|
||||||
|
addComponent(volume);
|
||||||
|
addComponent(playButton);
|
||||||
|
playButton.setRadioGroupId(1);
|
||||||
|
playButton.setImages(image.get());
|
||||||
|
playButton.setToggleState(false, juce::NotificationType::dontSendNotification);
|
||||||
|
playButton.setColour(juce::TextButton::buttonColourId, juce::Colours::lightgreen);
|
||||||
|
playButton.setColour(juce::TextButton::textColourOffId, juce::Colours::black);
|
||||||
|
playButton.setLookAndFeel(lef.get());
|
||||||
|
volume.setRange(-100, 6); // on prend -100db en min, et on s'autorise à amplifier le sample à +6db
|
||||||
|
volume.setValue(0); // valeur initiale à 0db
|
||||||
|
volume.setSliderStyle(juce::Slider::SliderStyle::LinearVertical);
|
||||||
|
volume.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, true, 0, 0);
|
||||||
|
volume.setColour(juce::Slider::thumbColourId, backgroundColour);
|
||||||
|
volume.setColour(juce::Slider::trackColourId, juce::Colours::green);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayPanel::~PlayPanel()
|
||||||
|
{
|
||||||
|
// evite une fuite mémoire à la fermeture due au L&F
|
||||||
|
playButton.setLookAndFeel(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPanel::addComponent(juce::Component& c)
|
||||||
|
{
|
||||||
|
addAndMakeVisible(c);
|
||||||
|
c.setColour(juce::TextButton::buttonColourId, backgroundColour);
|
||||||
|
c.setColour(juce::TextButton::textColourOffId, juce::Colours::black);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPanel::paint(juce::Graphics& g)
|
||||||
|
{
|
||||||
|
g.fillAll(backgroundColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPanel::resized()
|
||||||
|
{
|
||||||
|
// On gêre le placement via une grille 3 collones
|
||||||
|
// la première colonne est vide et sert de "marge"
|
||||||
|
juce::Grid vpgrid;
|
||||||
|
using Track = juce::Grid::TrackInfo;
|
||||||
|
using Fr = juce::Grid::Fr;
|
||||||
|
vpgrid.templateRows = { Track(Fr(1)) };
|
||||||
|
vpgrid.templateColumns = { Track(Fr(1)), Track(Fr(5)), Track(Fr(1)) };
|
||||||
|
|
||||||
|
vpgrid.items = { juce::GridItem(), juce::GridItem(playButton), juce::GridItem(volume) };
|
||||||
|
vpgrid.performLayout(getLocalBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPanel::init(Metronome& metro)
|
||||||
|
{
|
||||||
|
// On établi les liaisons entre le Metronome et les composants graphique
|
||||||
|
playButton.onClick = [&, this]() { play(metro); };
|
||||||
|
volume.setValue(juce::Decibels::gainToDecibels(metro.getGain())); // On récupère la valeur initiale du gain
|
||||||
|
volume.onValueChange = [&, this]
|
||||||
|
{
|
||||||
|
double db = volume.getValue();
|
||||||
|
metro.setGain((float)juce::Decibels::decibelsToGain(db));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPanel::play(Metronome& metro)
|
||||||
|
{
|
||||||
|
// lance la lecture ou met en pause
|
||||||
|
playState = playState == PlayState::playing ? PlayState::stopped : PlayState::playing;
|
||||||
|
if (playState == PlayState::stopped)
|
||||||
|
{
|
||||||
|
// en cas de stop, on remet les compteurs à zero sur le metronome
|
||||||
|
metro.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// On modifie la couleur du bouton en fonction de l'état
|
||||||
|
playButton.setColour(juce::TextButton::buttonColourId, playState == PlayState::playing ? juce::Colours::green : juce::Colours::lightgreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPanel::highlight()
|
||||||
|
{
|
||||||
|
// Met le bouton en surbrillance sur le temps
|
||||||
|
// la variable highlightFrames decompte 6 frames sur un timer de 20ms soit 120ms
|
||||||
|
if (playState == PlayState::playing)
|
||||||
|
{
|
||||||
|
if (highlightFrames > 0)
|
||||||
|
{
|
||||||
|
playButton.setColour(juce::TextButton::buttonColourId, juce::Colours::orangered);
|
||||||
|
highlightFrames--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
playButton.setColour(juce::TextButton::buttonColourId, juce::Colours::green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion du bouton play et du volume.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "Metronome.h"
|
||||||
|
|
||||||
|
// Etat de lecture
|
||||||
|
enum class PlayState
|
||||||
|
{
|
||||||
|
playing,
|
||||||
|
stopped
|
||||||
|
};
|
||||||
|
|
||||||
|
// surcharge du look&feel pour dessiner le bouton play en cercle
|
||||||
|
struct RoundButtonLookAndFeel : public juce::LookAndFeel_V4
|
||||||
|
{
|
||||||
|
void drawButtonBackground(juce::Graphics& g, juce::Button& button, const juce::Colour& backgroundColour,
|
||||||
|
bool isMouseOverButton, bool isButtonDown) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// classe principale
|
||||||
|
struct PlayPanel : public juce::Component
|
||||||
|
{
|
||||||
|
explicit PlayPanel(juce::Colour c);
|
||||||
|
~PlayPanel();
|
||||||
|
|
||||||
|
// Méthode de surcharge JUCE
|
||||||
|
void paint(juce::Graphics& g) override;
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
// utilitaire graphique
|
||||||
|
void addComponent(juce::Component& c);
|
||||||
|
|
||||||
|
// méthodes de gestion du lecteur
|
||||||
|
void init(Metronome& metro);
|
||||||
|
void play(Metronome& metro);
|
||||||
|
void highlight();
|
||||||
|
|
||||||
|
// composants graphiques
|
||||||
|
juce::Colour backgroundColour;
|
||||||
|
juce::Slider volume;
|
||||||
|
juce::DrawableButton playButton { "Play", juce::DrawableButton::ImageOnButtonBackground };
|
||||||
|
std::unique_ptr <RoundButtonLookAndFeel> lef;
|
||||||
|
|
||||||
|
// variables internes
|
||||||
|
int highlightFrames;
|
||||||
|
PlayState playState{ PlayState::stopped };
|
||||||
|
};
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
Copyright 2022 Nicolas Chambert
|
||||||
|
|
||||||
|
This program is free software : you can redistribute itand /or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program.If not, see < http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion du nombre de temps par mesure.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
#include "TempoPanel.h"
|
||||||
|
#include "Metronome.h"
|
||||||
|
|
||||||
|
TempoPanel::TempoPanel(juce::Colour c) : backgroundColour(c)
|
||||||
|
{
|
||||||
|
// initialisation de l'interface
|
||||||
|
addComponent(decrement);
|
||||||
|
addComponent(tempo);
|
||||||
|
addComponent(increment);
|
||||||
|
tempo.setText("4", juce::dontSendNotification); // On met une valeur mais elle va être RaZ dans le init()
|
||||||
|
tempo.getFont().setBold(true);
|
||||||
|
tempo.setColour(juce::Label::textColourId, juce::Colours::black);
|
||||||
|
tempo.setJustificationType(juce::Justification::centred);
|
||||||
|
tempo.setFont(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempoPanel::addComponent(juce::Component& c)
|
||||||
|
{
|
||||||
|
addAndMakeVisible(c);
|
||||||
|
c.setColour(juce::TextButton::buttonColourId, backgroundColour);
|
||||||
|
c.setColour(juce::TextButton::textColourOffId, juce::Colours::black);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempoPanel::paint(juce::Graphics& g)
|
||||||
|
{
|
||||||
|
g.fillAll(backgroundColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempoPanel::resized()
|
||||||
|
{
|
||||||
|
// On gêre le layout à la main
|
||||||
|
auto area = getLocalBounds();
|
||||||
|
int butHeigth = 20;
|
||||||
|
int butWidth = 48;
|
||||||
|
int labeSize = 3 * butHeigth;
|
||||||
|
int totalWidth = 2 * butWidth + labeSize;
|
||||||
|
int marginH = (area.getWidth() - totalWidth) / 2;
|
||||||
|
int marginV = (area.getHeight() - labeSize) / 2;
|
||||||
|
decrement.setBounds(area.getX() + marginH, area.getY() + marginV + butHeigth, butWidth, butHeigth);
|
||||||
|
tempo.setBounds(area.getX() + marginH + butWidth, area.getY() + marginV, labeSize, labeSize);
|
||||||
|
increment.setBounds(area.getX() + marginH + butWidth + labeSize, area.getY() + marginV + butHeigth, butWidth, butHeigth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempoPanel::init(Metronome& metro)
|
||||||
|
{
|
||||||
|
// On établi les liaisons entre le Metronome et les composants graphique
|
||||||
|
tempo.setText(juce::String(metro.getMeasures()), juce::dontSendNotification);
|
||||||
|
increment.onClick = [&, this]() { inc(metro); };
|
||||||
|
decrement.onClick = [&, this]() { dec(metro); };
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempoPanel::inc(Metronome& metro)
|
||||||
|
{
|
||||||
|
// incrémente le tempo
|
||||||
|
metro.setMeasures(metro.getMeasures() + 1);
|
||||||
|
tempo.setText(juce::String(metro.getMeasures()), juce::dontSendNotification); // MàJ du label
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempoPanel::dec(Metronome& metro)
|
||||||
|
{
|
||||||
|
// decremente le tempo
|
||||||
|
metro.setMeasures(metro.getMeasures() - 1);
|
||||||
|
tempo.setText(juce::String(metro.getMeasures()), juce::dontSendNotification); // MàJ du label
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Gestion du nombre de temps par mesure.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
#include "Metronome.h"
|
||||||
|
|
||||||
|
struct TempoPanel : public juce::Component
|
||||||
|
{
|
||||||
|
explicit TempoPanel(juce::Colour c);
|
||||||
|
|
||||||
|
// Méthode de surcharge JUCE
|
||||||
|
void paint(juce::Graphics& g) override;
|
||||||
|
void resized() override;
|
||||||
|
|
||||||
|
// utilitaire graphique
|
||||||
|
void addComponent(juce::Component& c);
|
||||||
|
|
||||||
|
// méthodes de gestion du tempo
|
||||||
|
void init(Metronome& metro);
|
||||||
|
void inc(Metronome& metro);
|
||||||
|
void dec(Metronome& metro);
|
||||||
|
|
||||||
|
// composants graphiques
|
||||||
|
juce::Colour backgroundColour;
|
||||||
|
juce::TextButton increment{ "+" };
|
||||||
|
juce::TextButton decrement{ "-" };
|
||||||
|
juce::Label tempo;
|
||||||
|
};
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<JUCERPROJECT id="eEbCxS" name="Tchick" projectType="guiapp" useAppConfig="0"
|
||||||
|
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" displaySplashScreen="1"
|
||||||
|
cppLanguageStandard="latest" splashScreenColour="Light" includeBinaryInJuceHeader="0">
|
||||||
|
<MAINGROUP id="gPzZc8" name="Tchick">
|
||||||
|
<GROUP id="{EDCE3033-ED90-D340-9E42-D702C9B82B08}" name="Assets">
|
||||||
|
<GROUP id="{A07629F7-9E5A-559D-9755-2EB795077D4C}" name="Clicks">
|
||||||
|
<GROUP id="{778AD36A-ADB9-BCE2-49CC-77E288129AEA}" name="01 SeikoSQ50">
|
||||||
|
<FILE id="C9jq8N" name="High Seiko SQ50.wav" compile="0" resource="0"
|
||||||
|
file="Assets/Clicks/01 SeikoSQ50/High Seiko SQ50.wav"/>
|
||||||
|
<FILE id="uQbK7f" name="Low Seiko SQ50.wav" compile="0" resource="0"
|
||||||
|
file="Assets/Clicks/01 SeikoSQ50/Low Seiko SQ50.wav"/>
|
||||||
|
</GROUP>
|
||||||
|
<GROUP id="{AE41D956-97BE-1D29-5301-286FB4B56301}" name="02 Stick">
|
||||||
|
<FILE id="CM52vr" name="High Stick.wav" compile="0" resource="0" file="Assets/Clicks/02 Stick/High Stick.wav"/>
|
||||||
|
<FILE id="zawixh" name="Low Stick.wav" compile="0" resource="0" file="Assets/Clicks/02 Stick/Low Stick.wav"/>
|
||||||
|
</GROUP>
|
||||||
|
<GROUP id="{D75593D1-F482-E1B6-CA27-988E2524442D}" name="03 Cowbell">
|
||||||
|
<FILE id="TjWHWd" name="High Cowbell.wav" compile="0" resource="0"
|
||||||
|
file="Assets/Clicks/03 Cowbell/High Cowbell.wav"/>
|
||||||
|
<FILE id="U48pfg" name="Low Cowbell.wav" compile="0" resource="0" file="Assets/Clicks/03 Cowbell/Low Cowbell.wav"/>
|
||||||
|
</GROUP>
|
||||||
|
<GROUP id="{A40DDBDF-ED54-59C0-CFCD-43194EE8C07A}" name="04 vChip Wood">
|
||||||
|
<FILE id="j6eFYY" name="High Wood.wav" compile="0" resource="0" file="Assets/Clicks/04 vChip Wood/High Wood.wav"/>
|
||||||
|
<FILE id="TNRBe5" name="Low Wood.wav" compile="0" resource="0" file="Assets/Clicks/04 vChip Wood/Low Wood.wav"/>
|
||||||
|
</GROUP>
|
||||||
|
<GROUP id="{157DA364-7F83-712F-1AFC-FB74BC4B3E23}" name="05 vChip Claves">
|
||||||
|
<FILE id="she6pj" name="HIgh Claves.wav" compile="0" resource="0" file="Assets/Clicks/05 vChip Claves/HIgh Claves.wav"/>
|
||||||
|
<FILE id="EC66R0" name="Low Claves.wav" compile="0" resource="0" file="Assets/Clicks/05 vChip Claves/Low Claves.wav"/>
|
||||||
|
</GROUP>
|
||||||
|
</GROUP>
|
||||||
|
</GROUP>
|
||||||
|
<GROUP id="{1F26F6FE-63EA-7E00-50D6-9178F972F8B2}" name="Source">
|
||||||
|
<FILE id="GSXFyS" name="OptionsPanel.cpp" compile="1" resource="0"
|
||||||
|
file="Source/OptionsPanel.cpp"/>
|
||||||
|
<FILE id="rNkF5a" name="TempoPanel.h" compile="0" resource="0" file="Source/TempoPanel.h"/>
|
||||||
|
<FILE id="EoRTt3" name="TempoPanel.cpp" compile="1" resource="0" file="Source/TempoPanel.cpp"/>
|
||||||
|
<FILE id="sl7izB" name="PlayPanel.h" compile="0" resource="0" file="Source/PlayPanel.h"/>
|
||||||
|
<FILE id="aswza3" name="PlayPanel.cpp" compile="1" resource="0" file="Source/PlayPanel.cpp"/>
|
||||||
|
<FILE id="TPOq96" name="OptionsPanel.h" compile="0" resource="0" file="Source/OptionsPanel.h"/>
|
||||||
|
<FILE id="bEvzCW" name="MesurePanel.h" compile="0" resource="0" file="Source/MesurePanel.h"/>
|
||||||
|
<FILE id="s74lBs" name="MesurePanel.cpp" compile="1" resource="0" file="Source/MesurePanel.cpp"/>
|
||||||
|
<FILE id="Ww74sz" name="Figures.h" compile="0" resource="0" file="Source/Figures.h"/>
|
||||||
|
<FILE id="QS0xaD" name="Config.h" compile="0" resource="0" file="Source/Config.h"/>
|
||||||
|
<FILE id="JbnJt4" name="Config.cpp" compile="1" resource="0" file="Source/Config.cpp"/>
|
||||||
|
<FILE id="PAwDsp" name="BPMPanel.h" compile="0" resource="0" file="Source/BPMPanel.h"/>
|
||||||
|
<FILE id="fz3AGQ" name="BPMPanel.cpp" compile="1" resource="0" file="Source/BPMPanel.cpp"/>
|
||||||
|
<FILE id="RqMHaF" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/>
|
||||||
|
<FILE id="lKOoJh" name="Metronome.h" compile="0" resource="0" file="Source/Metronome.h"/>
|
||||||
|
<FILE id="A0wYoj" name="Metronome.cpp" compile="1" resource="0" file="Source/Metronome.cpp"/>
|
||||||
|
<FILE id="WW9a1R" name="MainComponent.h" compile="0" resource="0" file="Source/MainComponent.h"/>
|
||||||
|
<FILE id="qf5GwL" name="MainComponent.cpp" compile="1" resource="0"
|
||||||
|
file="Source/MainComponent.cpp"/>
|
||||||
|
</GROUP>
|
||||||
|
</MAINGROUP>
|
||||||
|
<JUCEOPTIONS JUCE_STRICT_REFCOUNTEDPOINTER="1" JUCE_JACK="1"/>
|
||||||
|
<EXPORTFORMATS>
|
||||||
|
<VS2022 targetFolder="./.">
|
||||||
|
<CONFIGURATIONS>
|
||||||
|
<CONFIGURATION isDebug="1" name="Debug" prebuildCommand="xcopy /s /y "$(SolutionDir)Assets\Clicks" "$(OutDir)\Clicks\""/>
|
||||||
|
<CONFIGURATION isDebug="0" name="Release" prebuildCommand="xcopy /s /y "$(SolutionDir)Assets\Clicks" "$(OutDir)\Clicks\""/>
|
||||||
|
</CONFIGURATIONS>
|
||||||
|
<MODULEPATHS>
|
||||||
|
<MODULEPATH id="juce_audio_basics" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_audio_devices" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_audio_formats" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_core" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_data_structures" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_events" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_graphics" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_gui_basics" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_audio_utils" path="C:/Temp/JUCE/modules"/>
|
||||||
|
<MODULEPATH id="juce_audio_processors" path="C:/Temp/JUCE/modules"/>
|
||||||
|
<MODULEPATH id="juce_gui_extra" path="C:/Temp/JUCE/modules"/>
|
||||||
|
</MODULEPATHS>
|
||||||
|
</VS2022>
|
||||||
|
<LINUX_MAKE targetFolder="./.">
|
||||||
|
<CONFIGURATIONS>
|
||||||
|
<CONFIGURATION isDebug="1" name="Debug"/>
|
||||||
|
<CONFIGURATION isDebug="0" name="Release"/>
|
||||||
|
</CONFIGURATIONS>
|
||||||
|
<MODULEPATHS>
|
||||||
|
<MODULEPATH id="juce_audio_basics" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_audio_devices" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_audio_formats" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_core" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_data_structures" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_events" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_graphics" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_gui_basics" path="../../juce"/>
|
||||||
|
<MODULEPATH id="juce_audio_utils" path="C:/Temp/JUCE/modules"/>
|
||||||
|
<MODULEPATH id="juce_audio_processors" path="C:/Temp/JUCE/modules"/>
|
||||||
|
<MODULEPATH id="juce_gui_extra" path="C:/Temp/JUCE/modules"/>
|
||||||
|
</MODULEPATHS>
|
||||||
|
</LINUX_MAKE>
|
||||||
|
</EXPORTFORMATS>
|
||||||
|
<MODULES>
|
||||||
|
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_core" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_events" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
|
||||||
|
</MODULES>
|
||||||
|
<LIVE_SETTINGS>
|
||||||
|
<LINUX/>
|
||||||
|
</LIVE_SETTINGS>
|
||||||
|
</JUCERPROJECT>
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tchick - App", "Tchick_App.vcxproj", "{F9D7600C-859C-26DC-FE32-2EB5A98F1734}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{F9D7600C-859C-26DC-FE32-2EB5A98F1734}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{F9D7600C-859C-26DC-FE32-2EB5A98F1734}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{F9D7600C-859C-26DC-FE32-2EB5A98F1734}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{F9D7600C-859C-26DC-FE32-2EB5A98F1734}.Release|x64.Build.0 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
+3235
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup />
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma code_page(65001)
|
||||||
|
|
||||||
|
#ifdef JUCE_USER_DEFINED_RC_FILE
|
||||||
|
#include JUCE_USER_DEFINED_RC_FILE
|
||||||
|
#else
|
||||||
|
|
||||||
|
#undef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
VS_VERSION_INFO VERSIONINFO
|
||||||
|
FILEVERSION 1,0,0,0
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904E4"
|
||||||
|
BEGIN
|
||||||
|
VALUE "FileDescription", "Tchick\0"
|
||||||
|
VALUE "FileVersion", "1.0.0\0"
|
||||||
|
VALUE "ProductName", "Tchick\0"
|
||||||
|
VALUE "ProductVersion", "1.0.0\0"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x409, 1252
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user