Compare commits

..

16 Commits
gtk3 ... master

Author SHA1 Message Date
bf56da603c - Add bank MSB/LSB support,
- Add Alt_L key modifier support.
2022-04-23 13:28:54 +02:00
c8ecfea6a0 - fix compilation with format security checks. 2022-04-23 12:35:30 +02:00
1e117672bb - Fix Authors
- Drop CMake
- Update ReadMe
2022-04-23 12:30:22 +02:00
Markus Schmidt
fc73032929 Screenshot using standard 96dpi font size AND displaying velocity press m( 2018-03-12 22:27:33 +01:00
Markus Schmidt
1c014ac587 Screenshot using standard 96dpi font size 2018-03-12 22:25:08 +01:00
Markus Schmidt
3a1e5f32e1 Remove redundant themeing params; bigger font size; layout more compact; Cleanup 2018-03-12 22:22:28 +01:00
Markus Schmidt
5a11a96ab6 New icons matching the new keyboard style including SVG source 2018-03-12 22:10:44 +01:00
Markus Schmidt
8f57f93210 AUTHORS 2018-03-12 21:53:11 +01:00
Markus Schmidt
3b22be9dba New dark GTK style 2018-03-12 21:52:33 +01:00
Markus Schmidt
a006a81149 User interface:
* New layout
* New modulation fader
* New pitch bend fader
* New panic button
2018-03-12 21:47:15 +01:00
Markus Schmidt
fee161a38d Event handler for modulation, pitch bend and panic button 2018-03-12 21:43:54 +01:00
Markus Schmidt
b5c06ee4e6 New keyboard design:
* making use of Cairo
* displaying pressed keys via small indicator
* displaying velocity of pressed key
2018-03-12 21:30:39 +01:00
Markus Schmidt
32fba4955c Build environment autoconf; gitignore; (both taken from user Stazed on github with some modifications) 2018-03-12 13:36:20 +01:00
Markus Schmidt
ddd9cddc4a Get screenshot name right #2 m( 2018-03-12 13:05:50 +01:00
Markus Schmidt
bffc7e1883 Get screenshot name right 2018-03-12 13:04:39 +01:00
Markus Schmidt
93d31d76d5 README: markdown, screenshot, contact 2018-03-12 13:03:26 +01:00
21 changed files with 1061 additions and 635 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
*.[oa]
Makefile.in
Makefile
config.h
/build
/man/Makefile.in
/pixmaps/Makefile.in
/src/Makefile.in
/src/.deps/
/src/jack-keyboard
stamp-h1
config.h.in
config.log
config.status
configure
depcomp
install-sh
missing
aclocal.m4
autom4te.cache
compile

View File

@ -2,3 +2,5 @@ Edward Tomasz Napierała <trasz@FreeBSD.org>
Hans Petter Selasky <hselasky@FreeBSD.org>
Jeff Snyder <jeff@caffeinated.me.uk>
Dan Muresan <danmbox@gmail.com>
Markus Schmidt <schmidt@boomshop.net>
AlkorZ3 <alkorz2@rx3.net>

View File

@ -1,91 +0,0 @@
#
# Copyright (c) 2011 Hans Petter Selasky. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# Makefile for Jack Keyboard
#
cmake_minimum_required(VERSION 2.8)
option(ENABLE_GTK2 "Enable GTK2 Version" ON)
option(ENABLE_GTK3 "Enable GTK3 Version" OFF)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/")
set(VERSION "2.7.2")
set(JackEnable ON CACHE BOOL "Enable support for Jack")
set(LashEnable ON CACHE BOOL "Enable support for Lash")
set(X11Enable ON CACHE BOOL "Enable support for X11")
project(jack-keyboard)
add_executable(jack-keyboard src/jack-keyboard src/pianokeyboard)
find_package(PkgConfig REQUIRED)
if(ENABLE_GTK3)
set(ENABLE_GTK2 OFF)
PKG_CHECK_MODULES(GTK3 "gtk+-3.0 >= 3.16" REQUIRED)
include_directories(${GTK3_INCLUDE_DIRS})
target_link_libraries(jack-keyboard ${GTK3_LIBRARIES})
endif()
if(ENABLE_GTK2)
PKG_CHECK_MODULES(GTK2 "gtk+-2.0 >= 2.20" REQUIRED)
include_directories(${GTK2_INCLUDE_DIRS})
target_link_libraries(jack-keyboard ${GTK2_LIBRARIES})
endif()
# In preparation for GTK+3:
# Set GDK/GTK+ flags to report deprecated includes and symbols.
set(GTK_DEPRECATED_FLAGS "-DGTK_DISABLE_SINGLE_INCLUDES \
-DGDK_DISABLE_DEPRECATED \
-DGTK_DISABLE_DEPRECATED \
-DGSEAL_ENABLE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GTK_DEPRECATED_FLAGS}")
if(JackEnable)
find_package(JACK)
include_directories(${JACK_INCLUDE_DIR})
target_link_libraries(jack-keyboard ${JACK_LIBRARIES})
add_definitions(-DHAVE_JACK=1)
endif()
if(LashEnable)
find_package(LASH)
include_directories(${LASH_INCLUDE_DIR})
target_link_libraries(jack-keyboard ${LASH_LIBRARIES})
add_definitions(-DHAVE_LASH=1)
endif()
if(X11Enable)
find_package(X11)
include_directories(${X11_INCLUDE_DIR})
target_link_libraries(jack-keyboard ${X11_LIBRARIES})
add_definitions(-DHAVE_X11=1)
endif()
install(TARGETS jack-keyboard RUNTIME DESTINATION bin)
install(FILES pixmaps/jack-keyboard.png DESTINATION share/pixmaps)
install(FILES src/jack-keyboard.desktop DESTINATION share/applications)
install(FILES man/jack-keyboard.1 DESTINATION man/man1)

View File

@ -1,48 +0,0 @@
VERSION?=2.7.2
help:
@echo "Targets: configure all clean install package"
configure:
rm -rf build
mkdir build
cd build ; cmake ..
all:
make -C build all
install:
make -C build install
clean:
make -C build clean
package:
make -C build clean || echo -n
tar -cvf temp.tar --exclude="*~" --exclude="*#" \
--exclude=".svn" --exclude="*.orig" --exclude="*.rej" \
AUTHORS \
CMakeLists.txt \
COPYING \
Makefile \
NEWS \
README \
TODO \
cmake \
man \
pixmaps \
src
rm -rf jack-keyboard-${VERSION}
mkdir jack-keyboard-${VERSION}
tar -xvf temp.tar -C jack-keyboard-${VERSION}
rm -rf temp.tar
tar -zcvf jack-keyboard-${VERSION}.tar.gz jack-keyboard-${VERSION}

2
Makefile.am Normal file
View File

@ -0,0 +1,2 @@
SUBDIRS = src man pixmaps

6
NEWS
View File

@ -1,3 +1,9 @@
User-visible changes between 2.8.0 and 2.8.1 include:
- Add bank MSB/LSB support
User-visible changes between 2.7.2 and 2.8.0 include:
- Drop CMake and now use autotools
User-visible changes between 2.7.1 and 2.7.2 include:
User-visible changes between 2.6 and 2.7.1 include:

5
README
View File

@ -15,8 +15,9 @@ If you're using FreeBSD, install from ports - audio/jack-keyboard.
Otherwise, you need JACK with MIDI support, gtk+ 2.6 or higher,
make(1), gcc and all the standard headers. Untar the file, type
'make install' and that's it. If there is any problem, drop me
an email (trasz@FreeBSD.org) and I will help you. Really.
'make install' and that's it.
If Makefile is not present, you can generate it with:
aclocal && autoheader && automake --add-missing && autoconf && ./configure
How to use it?
--------------

128
README.md Normal file
View File

@ -0,0 +1,128 @@
# jack-keyboard
![Screenshot of jack-keyboard](jack_keyboard.png)
## What is it?
jack-keyboard is a virtual MIDI keyboard - a program that allows you to
send JACK MIDI events (play ;-) using your PC keyboard. It's somewhat
similar to vkeybd, except it uses JACK MIDI instead of ALSA, and the
keyboard mapping is much better - it uses the same layout as trackers
(like Impulse Tracker) did, so you have two and half octaves under your
fingers.
## How to compile it?
If you're using FreeBSD, install from ports - audio/jack-keyboard.
Otherwise, you need JACK with MIDI support, gtk+ 2.6 or higher,
make(1), gcc and all the standard headers. Untar the file, type
'make install' and that's it.
If Makefile is not present, you can generate it with:
aclocal && autoheader && automake --add-missing && autoconf && ./configure
## How to use it?
You need JACK with MIDI support and some softsynth that accepts
JACK MIDI as input. Ghostess, http://home.jps.net/~musound/,
is a good choice. Of course you will also need some DSSI plugin
that will make the actual sound. WhySynth is nice.
When you have all of these installed: first, run jackd. Then run
ghostess with a plugin of choice. Then run jack-keyboard. Press
'z' key. You should hear sound.
## Keyboard
Keyboard mapping is the same as in Impulse Tracker. This is your
QWERTY keyboard:
+----+----+ +----+----+----+ +----+----+
| 2 | 3 | | 5 | 6 | 7 | | 9 | 0 |
+----+----+----+----+----+----+----+----+----+----+
| q | w | e | r | t | y | u | i | o | p |
+----+----+----+----+----+----+----+----+----+----+
| s | d | | g | h | j |
+----+----+----+----+----+----+----+
| z | x | c | v | b | n | m |
+----+----+----+----+----+----+----+
And this is MIDI mapping.
+----+----+ +----+----+----+ +----+----+
|C#5 |D#5 | |F#5 |G#5 |A#5 | |C#6 |D#6 |
+----+----+----+----+----+----+----+----+----+----+
| C5 | D5 | E5 | F5 | G5 | A5 | B5 | C6 | D6 | E6 |
+----+----+----+----+----+----+----+----+----+----+
|C#4 |D#4 | |F#4 |G#4 |A#4 |
+----+----+----+----+----+----+----+
| C4 | D4 | E4 | F4 | G4 | A4 | B4 |
+----+----+----+----+----+----+----+
Spacebar is a sustain key. Holding it when pressing or releasing key
will make that key sustained, i.e. Note Off MIDI event won't be sent
after releasing the key. To release (stop) all the sustained notes,
press and release spacebar.
Holding Shift when pressing note will make it louder (it increases
velocity). Holding Ctrl will do the opposite. You can change the
default velocity by moving the Velocity slider. You can change the
"high" and "low" velocity values by moving the slider while holding
Shift or Ctrl keys.
Pressing "-" and "+" keys on numeric keypad changes the octave your
keyboard is mapped to. Pressing "*" and "/" on numeric keypad changes
MIDI program (instrument). Pressing Insert or Delete keys will connect
jack-keyboard to the next/previous MIDI input port (it will cycle
between running instances of ghostess, for example). Home and End keys
change the MIDI channel. Page Up and Page Down keys switch the MIDI
bank.
Esc works as a panic key - when you press it, all sound stops.
## Setting channel/bank/program number directly
To switch directly to a channel, bank or program, enter its number on
the numeric keypad (it won't be shown in any way) and press Home or End
(to change channel), Page Up or Page Down (to change bank) or "/" or
"*" (to change program). For example, to change to program number 123,
type, on the numeric keypad, "123/", without quotes.
## Titlebar
When -G xor -T is given, some informational messages in the title bar
appear. They are supposed to be self explanatory. If you see
"bank/program change not sent", it means that the bank/program numbers
as seen in the title bar were not sent. In other words, synth the
jack-keyboard is connected to may use different values. This happens
at startup and after switching between synths (using Insert/Delete
keys). To send bank/program change at startup, use -b and -p parame-
ters. To automatically send bank/program change after reconnect, use
the -u option.
## Pianola mode
In addition to the MIDI output port, jack-keyboard also opens MIDI
input (listening) port. MIDI events going into this port will be
passed to the output port unmodified, except for channel number, which
will be set to the one jack-keyboard is configured to use. Note On and
Note Off MIDI events will cause visible effect (pressing and releasing)
on keys, just like if they were being pressed using keyboard or mouse.
jack-keyboard will never connect to it's own MIDI input port. It will
also refuse to connect to any other client whose name begins in "jack-
keyboard", unless the "-k" option is given. It is, however, possible
to connect these ports manually, using jack_connect or qjackctl; this
may create feedback loop.
## License
JACK Keyboard is distributed under the BSD license, two clause.
## Contact
If you have any questions, comments, suggestions, patches or anything,
let us know:
* Edward Tomasz Napierala <trasz@FreeBSD.org> (initial version)
* Hans Petter Selasky <hselasky@FreeBSD.org> (further development, gtk3)
* Markus Schmidt <schmidt@boomshop.net> (further development, UX/UI)

2
TODO
View File

@ -1,7 +1,5 @@
Things to do, eventually:
- I know nothing about designing GUIs, and I'm afraid it shows. Redesign.
- Jack-keyboard does strange things to input focus and keyboard event
handling. Verify that it does not cause any problems.

View File

@ -1,28 +0,0 @@
# - Try to find Jack library
# Once done this will define:
# JACK_FOUND - system has JACK
# JACK_INCLUDE_DIR - incude paths to use
# JACK_LIBRARIES - Link these to use
SET(JACK_FOUND 0)
FIND_PATH(JACK_INCLUDE_DIR jack/ringbuffer.h
/usr/local/include
/usr/include
)
FIND_LIBRARY(JACK_LIBRARIES jack
/usr/local/lib
/usr/lib
)
# handle the QUIETLY and REQUIRED arguments and set JACK_FOUND to TRUE
# if all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(JACK DEFAULT_MSG JACK_LIBRARIES JACK_INCLUDE_DIR)
MARK_AS_ADVANCED(
JACK_INCLUDE_DIR
JACK_LIBRARIES
)

View File

@ -1,27 +0,0 @@
# - Try to find Lash library
# Once done this will define:
# LASH_FOUND - system has LASH
# LASH_INCLUDE_DIR - incude paths to use
# LASH_LIBRARIES - Link these to use
SET(LASH_FOUND 0)
FIND_PATH(LASH_INCLUDE_DIR lash/lash.h
/usr/local/include/lash-1.0
/usr/include/lash-1.0
)
FIND_LIBRARY(LASH_LIBRARIES lash
/usr/local/lib
/usr/lib
)
# handle the QUIETLY and REQUIRED arguments and set LASH_FOUND to TRUE
# if all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LASH DEFAULT_MSG LASH_LIBRARIES LASH_INCLUDE_DIR)
MARK_AS_ADVANCED(
LASH_INCLUDE_DIR
LASH_LIBRARIES
)

81
configure.ac Normal file
View File

@ -0,0 +1,81 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
AC_INIT([jack-keyboard], [2.8], [alkorz3@rx3.net])
AM_INIT_AUTOMAKE([-Wall foreign])
AC_CONFIG_SRCDIR([config.h.in])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h string.h sys/time.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_HEADER_TIME
AC_C_VOLATILE
# Checks for library functions.
AC_FUNC_MALLOC
AC_FUNC_STRTOD
AC_CHECK_FUNCS([gettimeofday memset strcasecmp strdup])
PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.2)
AC_SUBST(GTK_CFLAGS)
AC_SUBST(GTK_LIBS)
PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.2)
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.2)
AC_SUBST(GTHREAD_CFLAGS)
AC_SUBST(GTHREAD_LIBS)
AC_ARG_WITH([x11],
[AS_HELP_STRING([--with-x11],
[support keyboard grabbing @<:@default=check@:>@])],
[],
[with_x11=check])
AS_IF([test "x$with_x11" != xno],
[PKG_CHECK_MODULES(X11, x11, AC_DEFINE([HAVE_X11], [], [Defined if we have X11 support.]),
[if test "x$with_x11" != xcheck; then
AC_MSG_FAILURE([--with-x11 was given, but x11 was not found])
fi
])])
AC_SUBST(X11_CFLAGS)
AC_SUBST(X11_LIBS)
PKG_CHECK_MODULES(JACK, jack >= 0.102.0)
AC_SUBST(JACK_CFLAGS)
AC_SUBST(JACK_LIBS)
PKG_CHECK_MODULES(JACK_MIDI_NEEDS_NFRAMES, jack < 0.105.00, AC_DEFINE(JACK_MIDI_NEEDS_NFRAMES, 1, [whether or not JACK routines need nframes parameter]), true)
AC_ARG_WITH([lash],
[AS_HELP_STRING([--with-lash],
[support LASH @<:@default=check@:>@])],
[],
[with_lash=check])
AS_IF([test "x$with_lash" != xno],
[PKG_CHECK_MODULES(LASH, lash-1.0, AC_DEFINE([HAVE_LASH], [], [Defined if we have LASH support.]),
[if test "x$with_lash" != xcheck; then
AC_MSG_FAILURE([--with-lash was given, but LASH was not found])
fi
])])
AC_SUBST(LASH_CFLAGS)
AC_SUBST(LASH_LIBS)
AC_CONFIG_FILES([Makefile src/Makefile man/Makefile pixmaps/Makefile])
AC_OUTPUT

BIN
jack_keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

4
man/Makefile.am Normal file
View File

@ -0,0 +1,4 @@
man_MANS = jack-keyboard.1
EXTRA_DIST = $(man_MANS)

17
pixmaps/Makefile.am Normal file
View File

@ -0,0 +1,17 @@
pixmapsdir = $(datadir)/pixmaps
pixmaps_DATA = jack-keyboard.png
EXTRA_DIST = $(pixmaps_DATA)
gtk_update_icon_cache = gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
install-data-hook: update-icon-cache
uninstall-hook: update-icon-cache
update-icon-cache:
@-if test -z "$(DESTDIR)"; then \
echo "Updating Gtk icon cache."; \
$(gtk_update_icon_cache); \
else \
echo "*** Icon cache not updated. After (un)install, run this:"; \
echo "*** $(gtk_update_icon_cache)"; \
fi

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

After

Width:  |  Height:  |  Size: 1.4 KiB

205
pixmaps/jack-keyboard.svg Normal file
View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="256"
height="256"
viewBox="0 0 256 256"
id="svg2"
version="1.1"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="jack-keyboard.svg"
inkscape:export-filename="/home/markus/Programmierung/jack-keyboard/pixmaps/jack-keyboard.png"
inkscape:export-xdpi="48"
inkscape:export-ydpi="48">
<defs
id="defs">
<linearGradient
inkscape:collect="always"
id="linearGradient932">
<stop
style="stop-color:#ffffff;stop-opacity:0.38283062"
offset="0"
id="stop928" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop930" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient918">
<stop
style="stop-color:#ffffff;stop-opacity:0.07656612"
offset="0"
id="stop914" />
<stop
id="stop926"
offset="0.13388273"
style="stop-color:#ffffff;stop-opacity:0.66473317" />
<stop
id="stop922"
offset="0.46153846"
style="stop-color:#ffffff;stop-opacity:0.45011601" />
<stop
style="stop-color:#ffffff;stop-opacity:0.29234338"
offset="0.46153846"
id="stop924" />
<stop
style="stop-color:#ffffff;stop-opacity:0.23433875"
offset="1"
id="stop916" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient901">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop897" />
<stop
id="stop905"
offset="0.11764706"
style="stop-color:#4f4f4f;stop-opacity:1" />
<stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop899" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient828">
<stop
style="stop-color:#6f6f5d;stop-opacity:1"
offset="0"
id="stop824" />
<stop
id="stop832"
offset="0.125"
style="stop-color:#fbfbfb;stop-opacity:1" />
<stop
style="stop-color:#d7d6b6;stop-opacity:1"
offset="1"
id="stop826" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient828"
id="linearGradient830"
x1="128"
y1="0"
x2="128"
y2="256"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient901"
id="linearGradient903"
x1="128"
y1="0"
x2="128"
y2="136"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient918"
id="linearGradient920"
x1="128"
y1="0"
x2="128"
y2="104"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient932"
id="linearGradient934"
x1="8"
y1="0"
x2="8"
y2="80"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.44097686"
inkscape:cx="685.32502"
inkscape:cy="-202.20348"
inkscape:document-units="px"
inkscape:current-layer="svg2"
showgrid="true"
units="px"
inkscape:window-width="2880"
inkscape:window-height="1553"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:bbox-nodes="true"
inkscape:object-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-others="false"
showborder="false">
<inkscape:grid
type="xygrid"
id="grid1"
empspacing="8"
visible="false" />
</sodipodi:namedview>
<metadata
id="metadata">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient830);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.51181102;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect816"
width="256"
height="256"
x="0"
y="0" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.921;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient934);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.51181102;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 0 0 L 0 256 L 4 256 L 4 0 L 0 0 z M 64 0 L 64 256 L 68 256 L 68 0 L 64 0 z M 128 0 L 128 256 L 132 256 L 132 0 L 128 0 z M 192 0 L 192 256 L 196 256 L 196 0 L 192 0 z "
id="rect818" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.51300001;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.51181102;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 60,0 v 256 h 4 V 0 Z m 64,0 v 256 h 4 V 0 Z m 64,0 v 256 h 4 V 0 Z"
id="rect834"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccc" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient903);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.5118109;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 40 0 L 40 136 L 88 136 L 88 0 L 40 0 z M 104 0 L 104 136 L 152 136 L 152 0 L 104 0 z M 168 0 L 168 136 L 216 136 L 216 0 L 168 0 z "
id="rect820" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient920);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.51181102;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 48 0 L 48 104 L 80 104 L 80 0 L 48 0 z M 112 0 L 112 104 L 144 104 L 144 0 L 112 0 z M 176 0 L 176 104 L 208 104 L 208 0 L 176 0 z "
id="rect907" />
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

10
src/Makefile.am Normal file
View File

@ -0,0 +1,10 @@
bin_PROGRAMS = jack-keyboard
jack_keyboard_SOURCES = jack-keyboard.c pianokeyboard.c pianokeyboard.h
jack_keyboard_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(GTHREAD_LIBS) $(X11_LIBS) $(JACK_LIBS) $(LASH_LIBS)
jack_keyboard_CFLAGS = $(GTK_CFLAGS) $(GLIB_CFLAGS) $(GTHREAD_CFLAGS) $(X11_CFLAGS) $(JACK_CFLAGS) $(LASH_CFLAGS) \
-DG_LOG_DOMAIN=\"jack-keyboard\"
desktopdir = $(datadir)/applications
desktop_DATA = jack-keyboard.desktop
EXTRA_DIST = $(desktop_DATA)

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,8 @@
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <cairo.h>
#include <math.h>
#include "pianokeyboard.h"
@ -55,53 +57,34 @@ draw_keyboard_cue(PianoKeyboard *pk)
int w, h, first_note_in_lower_row, last_note_in_lower_row,
first_note_in_higher_row, last_note_in_higher_row;
GdkGC *gc;
w = pk->notes[0].w;
h = pk->notes[0].h;
gc = GTK_WIDGET(pk)->style->fg_gc[0];
first_note_in_lower_row = (pk->octave + 5) * 12;
last_note_in_lower_row = (pk->octave + 6) * 12 - 1;
first_note_in_higher_row = (pk->octave + 6) * 12;
last_note_in_higher_row = (pk->octave + 7) * 12 + 4;
cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(GTK_WIDGET(pk)));
gdk_draw_line(GTK_WIDGET(pk)->window, gc, pk->notes[first_note_in_lower_row].x + 3,
h - 6, pk->notes[last_note_in_lower_row].x + w - 3, h - 6);
cairo_move_to(cr, pk->notes[first_note_in_lower_row].x + 3, h - 6);
cairo_line_to(cr, pk->notes[last_note_in_lower_row].x + w - 3, h - 6);
cairo_move_to(cr, pk->notes[first_note_in_higher_row].x + 3, h - 9);
cairo_line_to(cr, pk->notes[last_note_in_higher_row].x + w - 3, h - 9);
cairo_destroy(cr);
gdk_draw_line(GTK_WIDGET(pk)->window, gc, pk->notes[first_note_in_higher_row].x + 3,
h - 9, pk->notes[last_note_in_higher_row].x + w - 3, h - 9);
}
static void
#if GTK_CHECK_VERSION(3,0,0)
draw_note(PianoKeyboard *pk, cairo_t *cr, int note)
#else
static void
draw_note(PianoKeyboard *pk, int note)
#endif
{
GtkWidget *widget = GTK_WIDGET(pk);
if (note < pk->min_note)
return;
if (note > pk->max_note)
return;
int is_white, x, w, h;
#if GTK_CHECK_VERSION(3,0,0)
if (!cr)
cr = gdk_cairo_create(gtk_widget_get_window(GTK_WIDGET(pk)));
GdkRGBA black = { .red = 0.0, .green = 0.0, .blue = 0.0, .alpha = 1.0};
GdkRGBA white = { .red = 1.0, .green = 1.0, .blue = 1.0, .alpha = 1.0};
#else
cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(GTK_WIDGET(pk)));
GdkColor black = {0, 0, 0, 0};
GdkColor white = {0, 65535, 65535, 65535};
GtkWidget *widget;
GtkAllocation allocation;
#endif
int is_white, x, w, h, pressed;
is_white = pk->notes[note].white;
@ -109,53 +92,23 @@ draw_note(PianoKeyboard *pk, int note)
w = pk->notes[note].w;
h = pk->notes[note].h;
if (pk->notes[note].pressed || pk->notes[note].sustained)
is_white = !is_white;
if (is_white)
#if GTK_CHECK_VERSION(3,0,0)
gdk_cairo_set_source_rgba(cr, &white);
#else
gdk_cairo_set_source_color(cr, &white);
#endif
else
#if GTK_CHECK_VERSION(3,0,0)
gdk_cairo_set_source_rgba(cr, &black);
#else
gdk_cairo_set_source_color(cr, &black);
#endif
cairo_rectangle(cr, x, 0, w, h);
cairo_fill(cr);
#if GTK_CHECK_VERSION(3,0,0)
gdk_cairo_set_source_rgba(cr, &black);
#else
gdk_cairo_set_source_color(cr, &black);
#endif
cairo_rectangle(cr, x, 0, w, h);
cairo_stroke(cr);
#if !GTK_CHECK_VERSION(3,0,0)
cairo_destroy(cr);
#endif
pressed = (int)(pk->notes[note].pressed || pk->notes[note].sustained);
if (is_white) {
piano_keyboard_draw_white_key(widget, x, 0, w, h, pressed, pk->notes[note].velocity);
} else {
piano_keyboard_draw_black_key(widget, x, 0, w, h, pressed, pk->notes[note].velocity);
}
if (note < NNOTES - 2 && !pk->notes[note + 1].white)
draw_note(pk, note + 1);
if (note > 0 && !pk->notes[note - 1].white)
draw_note(pk, note - 1);
if (pk->enable_keyboard_cue)
draw_keyboard_cue(pk);
/* We need to redraw black keys that partially obscure the white one. */
if (note < NNOTES - 2 && !pk->notes[note + 1].white)
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, cr, note + 1);
#else
draw_note(pk, note + 1);
#endif
if (note > 0 && !pk->notes[note - 1].white)
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, cr, note - 1);
#else
draw_note(pk, note - 1);
/*
* XXX: This doesn't really belong here. Originally I wanted to pack PianoKeyboard into GtkFrame
* packed into GtkAlignment. I failed to make it behave the way I want. GtkFrame would need
@ -163,14 +116,12 @@ draw_note(PianoKeyboard *pk, int note)
* that didn't work.
*/
widget = GTK_WIDGET(pk);
gtk_widget_get_allocation(widget, &allocation);
gtk_paint_shadow(gtk_widget_get_style(widget), gtk_widget_get_window(widget), GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL,
widget, NULL, pk->widget_margin, 0, allocation.width - pk->widget_margin * 2 + 1,
allocation.height);
#endif
gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL,
widget, NULL, pk->widget_margin, 0, widget->allocation.width - pk->widget_margin * 2 + 1,
widget->allocation.height);
}
static int
static int
press_key(PianoKeyboard *pk, int key)
{
assert(key >= 0);
@ -188,18 +139,15 @@ press_key(PianoKeyboard *pk, int key)
pk->notes[key].sustained = 0;
pk->notes[key].pressed = 1;
pk->notes[key].velocity = pk->current_velocity;
g_signal_emit_by_name(GTK_WIDGET(pk), "note-on", key);
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, NULL, key);
#else
draw_note(pk, key);
#endif
return (1);
}
static int
static int
release_key(PianoKeyboard *pk, int key)
{
assert(key >= 0);
@ -219,16 +167,12 @@ release_key(PianoKeyboard *pk, int key)
return (0);
g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", key);
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, NULL, key);
#else
draw_note(pk, key);
#endif
return (1);
}
static void
static void
stop_unsustained_notes(PianoKeyboard *pk)
{
int i;
@ -237,16 +181,12 @@ stop_unsustained_notes(PianoKeyboard *pk)
if (pk->notes[i].pressed && !pk->notes[i].sustained) {
pk->notes[i].pressed = 0;
g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, NULL, i);
#else
draw_note(pk, i);
#endif
}
}
}
static void
static void
stop_sustained_notes(PianoKeyboard *pk)
{
int i;
@ -256,11 +196,7 @@ stop_sustained_notes(PianoKeyboard *pk)
pk->notes[i].pressed = 0;
pk->notes[i].sustained = 0;
g_signal_emit_by_name(GTK_WIDGET(pk), "note-off", i);
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, NULL, i);
#else
draw_note(pk, i);
#endif
}
}
}
@ -294,7 +230,7 @@ clear_notes(PianoKeyboard *pk)
g_array_set_size(pk->key_bindings, 0);
}
static void
static void
bind_keys_qwerty(PianoKeyboard *pk)
{
clear_notes(pk);
@ -348,7 +284,7 @@ bind_keys_qwerty(PianoKeyboard *pk)
bind_key(pk, 51, 45); /* yes, really, at least here... */
}
static gint
static gint
keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer notused)
{
int note;
@ -376,14 +312,12 @@ keyboard_event_handler(GtkWidget *mk, GdkEventKey *event, gpointer notused)
return (TRUE);
}
static int
static int
get_note_for_xy(PianoKeyboard *pk, int x, int y)
{
int height, note;
GtkAllocation allocation;
gtk_widget_get_allocation(GTK_WIDGET(pk), &allocation);
height = allocation.height;
height = GTK_WIDGET(pk)->allocation.height;
if (y <= ((height * 2) / 3)) { /* might be a black key */
for (note = 0; note <= pk->max_note; ++note) {
@ -406,7 +340,7 @@ get_note_for_xy(PianoKeyboard *pk, int x, int y)
return (-1);
}
static gboolean
static gboolean
mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer notused)
{
int x, y, note;
@ -448,7 +382,7 @@ mouse_button_event_handler(PianoKeyboard *pk, GdkEventButton *event, gpointer no
return (TRUE);
}
static gboolean
static gboolean
mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer notused)
{
int note;
@ -459,7 +393,7 @@ mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer no
note = get_note_for_xy(pk, event->x, event->y);
if (note != pk->note_being_pressed_using_mouse && note >= 0) {
if (pk->note_being_pressed_using_mouse >= 0)
release_key(pk, pk->note_being_pressed_using_mouse);
press_key(pk, note);
@ -470,45 +404,23 @@ mouse_motion_event_handler(PianoKeyboard *pk, GdkEventMotion *event, gpointer no
}
static gboolean
#if GTK_CHECK_VERSION(3,0,0)
piano_keyboard_draw(GtkWidget *widget, cairo_t *cr)
#else
piano_keyboard_expose(GtkWidget *widget, GdkEventExpose *event)
#endif
{
int i;
PianoKeyboard *pk = PIANO_KEYBOARD(widget);
for (i = 0; i < NNOTES; i++)
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, NULL, i);
#else
draw_note(pk, i);
#endif
return (TRUE);
}
#if GTK_CHECK_VERSION(3,0,0)
static void
piano_keyboard_get_preferred_width(GtkWidget *widget, gint *minimal_width, gint *natural_width)
{
*minimal_width = *natural_width = PIANO_KEYBOARD_DEFAULT_WIDTH;
}
static void
piano_keyboard_get_preferred_height(GtkWidget *widget, gint *minimal_height, gint *natural_height)
{
*minimal_height = *natural_height = PIANO_KEYBOARD_DEFAULT_HEIGHT;
}
#else
static void
static void
piano_keyboard_size_request(GtkWidget *widget, GtkRequisition *requisition)
{
requisition->width = PIANO_KEYBOARD_DEFAULT_WIDTH;
requisition->height = PIANO_KEYBOARD_DEFAULT_HEIGHT;
}
#endif
static int is_black(int key)
{
@ -545,7 +457,6 @@ recompute_dimensions(PianoKeyboard *pk)
{
int number_of_white_keys = 0, skipped_white_keys = 0, key_width, black_key_width, useful_width, note,
white_key, width, height;
GtkAllocation allocation;
for (note = pk->min_note; note <= pk->max_note; ++note)
if (!is_black(note))
@ -554,9 +465,8 @@ recompute_dimensions(PianoKeyboard *pk)
if (!is_black(note))
++skipped_white_keys;
gtk_widget_get_allocation(GTK_WIDGET(pk), &allocation);
width = allocation.width;
height = allocation.height;
width = GTK_WIDGET(pk)->allocation.width;
height = GTK_WIDGET(pk)->allocation.height;
key_width = width / number_of_white_keys;
black_key_width = key_width * 0.8;
@ -566,11 +476,11 @@ recompute_dimensions(PianoKeyboard *pk)
for (note = 0, white_key = -skipped_white_keys; note < NNOTES; note++) {
if (is_black(note)) {
/* This note is black key. */
pk->notes[note].x = pk->widget_margin +
pk->notes[note].x = pk->widget_margin +
(white_key * key_width) -
(black_key_width * black_key_left_shift(note));
pk->notes[note].w = black_key_width;
pk->notes[note].h = (height * 2) / 3;
pk->notes[note].h = (height * 3) / 5;
pk->notes[note].white = 0;
continue;
}
@ -592,12 +502,12 @@ piano_keyboard_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
g_return_if_fail(widget != NULL);
g_return_if_fail(allocation != NULL);
gtk_widget_set_allocation(widget, allocation);
widget->allocation = *allocation;
recompute_dimensions(PIANO_KEYBOARD(widget));
if (gtk_widget_get_realized(widget))
gdk_window_move_resize (gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height);
if (GTK_WIDGET_REALIZED(widget))
gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
}
static void
@ -614,16 +524,10 @@ piano_keyboard_class_init(PianoKeyboardClass *klass)
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
widget_klass = (GtkWidgetClass*)klass;
widget_klass = (GtkWidgetClass*)klass;
#if GTK_CHECK_VERSION(3,0,0)
widget_klass->draw = piano_keyboard_draw;
widget_klass->get_preferred_width = piano_keyboard_get_preferred_width;
widget_klass->get_preferred_height = piano_keyboard_get_preferred_height;
#else
widget_klass->expose_event = piano_keyboard_expose;
widget_klass->size_request = piano_keyboard_size_request;
#endif
widget_klass->size_allocate = piano_keyboard_size_allocate;
}
@ -669,7 +573,7 @@ piano_keyboard_new(void)
GtkWidget *widget;
PianoKeyboard *pk;
widget = g_object_new(TYPE_PIANO_KEYBOARD, NULL);
widget = gtk_type_new(piano_keyboard_get_type());
pk = PIANO_KEYBOARD(widget);
pk->maybe_stop_sustained_notes = 0;
@ -702,7 +606,7 @@ piano_keyboard_sustain_press(PianoKeyboard *pk)
}
}
void
void
piano_keyboard_sustain_release(PianoKeyboard *pk)
{
if (pk->maybe_stop_sustained_notes)
@ -712,15 +616,12 @@ piano_keyboard_sustain_release(PianoKeyboard *pk)
}
void
piano_keyboard_set_note_on(PianoKeyboard *pk, int note)
piano_keyboard_set_note_on(PianoKeyboard *pk, int note, int vel)
{
if (pk->notes[note].pressed == 0) {
pk->notes[note].pressed = 1;
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, NULL, note);
#else
pk->notes[note].velocity = vel;
draw_note(pk, note);
#endif
}
}
@ -730,11 +631,7 @@ piano_keyboard_set_note_off(PianoKeyboard *pk, int note)
if (pk->notes[note].pressed || pk->notes[note].sustained) {
pk->notes[note].pressed = 0;
pk->notes[note].sustained = 0;
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, NULL, note);
#else
draw_note(pk, note);
#endif
}
}
@ -770,3 +667,137 @@ piano_keyboard_enable_all_midi_notes(PianoKeyboard* pk)
recompute_dimensions(pk);
}
void
piano_keyboard_draw_white_key (GtkWidget *widget, int x, int y, int w, int h, int pressed, int vel)
{
cairo_pattern_t *pat;
GdkWindow *window = widget->window;
cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
cairo_set_line_join(c, CAIRO_LINE_JOIN_MITER);
cairo_set_line_width(c, 1);
cairo_rectangle(c, x, y, w, h);
cairo_clip_preserve(c);
pat = cairo_pattern_create_linear (x, y, x, y + h);
cairo_pattern_add_color_stop_rgb (pat, 0.0, 0.25, 0.25, 0.2);
cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.957, 0.914, 0.925);
cairo_pattern_add_color_stop_rgb (pat, 1.0, 0.796, 0.787, 0.662);
cairo_set_source(c, pat);
cairo_fill(c);
cairo_move_to(c, x + 0.5, y);
cairo_line_to(c, x + 0.5, y + h);
cairo_set_source_rgba(c, 1, 1, 1, 0.75);
cairo_stroke(c);
cairo_move_to(c, x + w - 0.5, y);
cairo_line_to(c, x + w - 0.5, y + h);
cairo_set_source_rgba(c, 0, 0, 0, 0.5);
cairo_stroke(c);
if (pressed)
piano_keyboard_draw_pressed(c, x, y, w, h, vel);
piano_keyboard_draw_key_shadow(c, x, y, w, h);
cairo_destroy(c);
}
void
piano_keyboard_draw_black_key (GtkWidget *widget, int x, int y, int w, int h, int pressed, int vel)
{
cairo_pattern_t *pat;
GdkWindow *window = widget->window;
cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
cairo_set_line_join(c, CAIRO_LINE_JOIN_MITER);
cairo_set_line_width(c, 1);
cairo_rectangle(c, x, y, w, h);
cairo_clip_preserve(c);
pat = cairo_pattern_create_linear (x, y, x, y + h);
cairo_pattern_add_color_stop_rgb (pat, 0.0, 0, 0, 0);
cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.27, 0.27, 0.27);
cairo_pattern_add_color_stop_rgb (pat, 1.0, 0, 0, 0);
cairo_set_source(c, pat);
cairo_fill(c);
pat = cairo_pattern_create_linear (x + 1, y, x + 1, y + h - w);
cairo_pattern_add_color_stop_rgb (pat, 0.0, 0, 0, 0);
cairo_pattern_add_color_stop_rgb (pat, 0.1, 0.55, 0.55, 0.55);
cairo_pattern_add_color_stop_rgb (pat, 0.5, 0.45, 0.45, 0.45);
cairo_pattern_add_color_stop_rgb (pat, 0.5001, 0.35, 0.35, 0.35);
cairo_pattern_add_color_stop_rgb (pat, 1.0, 0.25, 0.25, 0.25);
cairo_set_source(c, pat);
cairo_rectangle(c, x + 1, y, w - 2, y + h - w);
cairo_fill(c);
if (pressed)
piano_keyboard_draw_pressed(c, x, y, w, h, vel);
piano_keyboard_draw_key_shadow(c, x, y, w, h);
cairo_destroy(c);
}
void
piano_keyboard_draw_pressed (cairo_t *c, int x, int y, int w, int h, int vel)
{
float m = w * .15; // margin
float s = w - m * 2.; // size
float _vel = ((float)vel / 127.);
float hue = _vel * 140 + 220; // hue 220 .. 360 - blue over pink to red
float sat = .5 + _vel * 0.3; // saturation 0.5 .. 0.8
float val = 1. - _vel * 0.2; // lightness 1.0 .. 0.8
cairo_rectangle(c, x + m, y + h - m - s * 2, s, s * 2);
hsv HSV = {hue, sat, val};
rgb RGB = hsv2rgb(HSV);
cairo_set_source_rgb(c, RGB.r, RGB.g, RGB.b);
cairo_fill(c);
}
void
piano_keyboard_draw_key_shadow (cairo_t *c, int x, int y, int w, int h)
{
cairo_pattern_t *pat;
pat = cairo_pattern_create_linear (x, y, x, y + (int)(h * 0.2));
cairo_pattern_add_color_stop_rgba (pat, 0.0, 0, 0, 0, 0.4);
cairo_pattern_add_color_stop_rgba (pat, 1.0, 0, 0, 0, 0);
cairo_rectangle(c, x, y, w, (int)(h * 0.2));
cairo_set_source(c, pat);
cairo_fill(c);
}
rgb
hsv2rgb(hsv HSV)
{
rgb RGB;
double H = HSV.h, S = HSV.s, V = HSV.v,
P, Q, T,
fract;
(H == 360.)?(H = 0.):(H /= 60.);
fract = H - floor(H);
P = V*(1. - S);
Q = V*(1. - S*fract);
T = V*(1. - S*(1. - fract));
if (0. <= H && H < 1.)
RGB = (rgb){.r = V, .g = T, .b = P};
else if (1. <= H && H < 2.)
RGB = (rgb){.r = Q, .g = V, .b = P};
else if (2. <= H && H < 3.)
RGB = (rgb){.r = P, .g = V, .b = T};
else if (3. <= H && H < 4.)
RGB = (rgb){.r = P, .g = Q, .b = V};
else if (4. <= H && H < 5.)
RGB = (rgb){.r = T, .g = P, .b = V};
else if (5. <= H && H < 6.)
RGB = (rgb){.r = V, .g = P, .b = Q};
else
RGB = (rgb){.r = 0., .g = 0., .b = 0.};
return RGB;
}

View File

@ -6,10 +6,10 @@
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@ -65,6 +65,7 @@ struct Note {
int w; /* Width of the key, in pixels. */
int h; /* Height of the key, in pixels. */
int white; /* 1 if key is white; 0 otherwise. */
int velocity;
};
struct _PianoKeyboard
@ -78,6 +79,7 @@ struct _PianoKeyboard
int note_being_pressed_using_mouse;
int min_note;
int max_note;
int current_velocity;
volatile struct Note notes[NNOTES];
/* Table used to translate from PC keyboard character to MIDI note number. */
GArray *key_bindings;
@ -88,16 +90,33 @@ struct _PianoKeyboardClass
GtkDrawingAreaClass parent_class;
};
typedef struct {
double r;
double g;
double b;
} rgb;
typedef struct {
double h;
double s;
double v;
} hsv;
GType piano_keyboard_get_type (void) G_GNUC_CONST;
GtkWidget* piano_keyboard_new (void);
void piano_keyboard_sustain_press (PianoKeyboard *pk);
void piano_keyboard_sustain_release (PianoKeyboard *pk);
void piano_keyboard_set_note_on (PianoKeyboard *pk, int note);
void piano_keyboard_set_note_on (PianoKeyboard *pk, int note, int vel);
void piano_keyboard_set_note_off (PianoKeyboard *pk, int note);
void piano_keyboard_set_keyboard_cue (PianoKeyboard *pk, int enabled);
void piano_keyboard_set_octave (PianoKeyboard *pk, int octave);
gboolean piano_keyboard_set_keyboard_layout (PianoKeyboard *pk, const char *layout);
void piano_keyboard_enable_all_midi_notes(PianoKeyboard* pk);
void piano_keyboard_draw_white_key (GtkWidget *widget, int x, int y, int w, int h, int pressed, int val);
void piano_keyboard_draw_black_key (GtkWidget *widget, int x, int y, int w, int h, int pressed, int val);
void piano_keyboard_draw_pressed (cairo_t *c, int x, int y, int w, int h, int val);
void piano_keyboard_draw_key_shadow (cairo_t *c, int x, int y, int w, int h);
rgb hsv2rgb(hsv HSV);
G_END_DECLS