Compare commits

..

10 Commits
master ... gtk3

Author SHA1 Message Date
Johannes Maibaum
2f5b69c021 [WIP] Allow building with GTK+ 3.16+.
Set CMake option `ENABLE_GTK3` to `ON` to build with GTK+ 3.0.

In GTK3 mode:

- Use `GtkGrid` instead of `GtkTable`
- Don't use `GtkMisc` to set `GtkLabel`'s alignments
- `PianoKeyboard`'s draw() code still needs to be refactored (doesn't
  show key presses).
- Grab keyboard functionality still needs to be ported (doesn't work
  yet).
2016-09-18 16:33:36 +02:00
Johannes Maibaum
44980fc2f5 Replace deprecated gtk_type_new with g_object_new. 2016-09-17 12:24:48 +02:00
Johannes Maibaum
c1fe0e7c15 sustain_event_handler listens to toggled signal.
This removes the deprecated calls to gtk_button_{pressed,released}.
2016-09-17 12:01:47 +02:00
Johannes Maibaum
f57e31dba5 Fix GSEAL_ENABLE errors for src/pianokeyboard.c. 2016-09-11 21:15:13 +02:00
Johannes Maibaum
6d5bbaca0a Use cairo for drawing. 2016-09-11 20:03:56 +02:00
Johannes Maibaum
2bd9ca2b9b Fix GSEAL_ENABLE errors for src/jack-keyboard.c. 2016-09-11 20:03:48 +02:00
Johannes Maibaum
2f4bdf615b Let GDK and GTK+ report deprecated stuff. 2016-09-11 20:03:41 +02:00
Johannes Maibaum
884ded8b95 Replace GDK_<keyname> with GDK_KEY_<keyname>. 2016-09-11 20:03:05 +02:00
Johannes Maibaum
0941f4abaa Remove calls to deprecated GLib functions.
GLib 2.32 (released in March 2012) deprecated `g_atexit()` [1] and
`g_thread_init()` [2].

[1]: https://developer.gnome.org/glib/stable/glib-Miscellaneous-Utility-Functions.html#g-atexit
[2]: https://developer.gnome.org/glib/stable/glib-Threads.html
2016-09-11 20:03:00 +02:00
Johannes Maibaum
bb80fe24b2 Remove trailing whitespace. 2016-09-11 20:02:53 +02:00
21 changed files with 638 additions and 1064 deletions

21
.gitignore vendored
View File

@ -1,21 +0,0 @@
*.[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,5 +2,3 @@ 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>

91
CMakeLists.txt Normal file
View File

@ -0,0 +1,91 @@
#
# 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)

48
Makefile Normal file
View File

@ -0,0 +1,48 @@
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}

View File

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

6
NEWS
View File

@ -1,9 +1,3 @@
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,9 +15,8 @@ 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
'make install' and that's it. If there is any problem, drop me
an email (trasz@FreeBSD.org) and I will help you. Really.
How to use it?
--------------

128
README.md
View File

@ -1,128 +0,0 @@
# 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,5 +1,7 @@
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.

28
cmake/FindJACK.cmake Normal file
View File

@ -0,0 +1,28 @@
# - 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
)

27
cmake/FindLASH.cmake Normal file
View File

@ -0,0 +1,27 @@
# - 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
)

View File

@ -1,81 +0,0 @@
# -*- 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

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

View File

@ -1,17 +0,0 @@
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: 1.4 KiB

After

Width:  |  Height:  |  Size: 161 B

View File

@ -1,205 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -1,10 +0,0 @@
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,8 +35,6 @@
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <cairo.h>
#include <math.h>
#include "pianokeyboard.h"
@ -57,34 +55,53 @@ 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;
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_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_higher_row].x + 3,
h - 9, pk->notes[last_note_in_higher_row].x + w - 3, h - 9);
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);
}
static void
static void
#if GTK_CHECK_VERSION(3,0,0)
draw_note(PianoKeyboard *pk, cairo_t *cr, int note)
#else
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, pressed;
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
is_white = pk->notes[note].white;
@ -92,23 +109,53 @@ draw_note(PianoKeyboard *pk, int note)
w = pk->notes[note].w;
h = pk->notes[note].h;
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->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
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
@ -116,12 +163,14 @@ draw_note(PianoKeyboard *pk, int note)
* that didn't work.
*/
widget = GTK_WIDGET(pk);
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);
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
}
static int
static int
press_key(PianoKeyboard *pk, int key)
{
assert(key >= 0);
@ -139,15 +188,18 @@ 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);
@ -167,12 +219,16 @@ 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;
@ -181,12 +237,16 @@ 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;
@ -196,7 +256,11 @@ 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
}
}
}
@ -230,7 +294,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);
@ -284,7 +348,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;
@ -312,12 +376,14 @@ 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;
height = GTK_WIDGET(pk)->allocation.height;
gtk_widget_get_allocation(GTK_WIDGET(pk), &allocation);
height = allocation.height;
if (y <= ((height * 2) / 3)) { /* might be a black key */
for (note = 0; note <= pk->max_note; ++note) {
@ -340,7 +406,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;
@ -382,7 +448,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;
@ -393,7 +459,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);
@ -404,23 +470,45 @@ 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);
}
static void
#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
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)
{
@ -457,6 +545,7 @@ 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))
@ -465,8 +554,9 @@ recompute_dimensions(PianoKeyboard *pk)
if (!is_black(note))
++skipped_white_keys;
width = GTK_WIDGET(pk)->allocation.width;
height = GTK_WIDGET(pk)->allocation.height;
gtk_widget_get_allocation(GTK_WIDGET(pk), &allocation);
width = allocation.width;
height = allocation.height;
key_width = width / number_of_white_keys;
black_key_width = key_width * 0.8;
@ -476,11 +566,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 * 3) / 5;
pk->notes[note].h = (height * 2) / 3;
pk->notes[note].white = 0;
continue;
}
@ -502,12 +592,12 @@ piano_keyboard_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
g_return_if_fail(widget != NULL);
g_return_if_fail(allocation != NULL);
widget->allocation = *allocation;
gtk_widget_set_allocation(widget, allocation);
recompute_dimensions(PIANO_KEYBOARD(widget));
if (GTK_WIDGET_REALIZED(widget))
gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
if (gtk_widget_get_realized(widget))
gdk_window_move_resize (gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height);
}
static void
@ -524,10 +614,16 @@ 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;
}
@ -573,7 +669,7 @@ piano_keyboard_new(void)
GtkWidget *widget;
PianoKeyboard *pk;
widget = gtk_type_new(piano_keyboard_get_type());
widget = g_object_new(TYPE_PIANO_KEYBOARD, NULL);
pk = PIANO_KEYBOARD(widget);
pk->maybe_stop_sustained_notes = 0;
@ -606,7 +702,7 @@ piano_keyboard_sustain_press(PianoKeyboard *pk)
}
}
void
void
piano_keyboard_sustain_release(PianoKeyboard *pk)
{
if (pk->maybe_stop_sustained_notes)
@ -616,12 +712,15 @@ piano_keyboard_sustain_release(PianoKeyboard *pk)
}
void
piano_keyboard_set_note_on(PianoKeyboard *pk, int note, int vel)
piano_keyboard_set_note_on(PianoKeyboard *pk, int note)
{
if (pk->notes[note].pressed == 0) {
pk->notes[note].pressed = 1;
pk->notes[note].velocity = vel;
#if GTK_CHECK_VERSION(3,0,0)
draw_note(pk, NULL, note);
#else
draw_note(pk, note);
#endif
}
}
@ -631,7 +730,11 @@ 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
}
}
@ -667,137 +770,3 @@ 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,7 +65,6 @@ 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
@ -79,7 +78,6 @@ 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;
@ -90,33 +88,16 @@ 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, int vel);
void piano_keyboard_set_note_on (PianoKeyboard *pk, int note);
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