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 961 additions and 310 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,69 +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)
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(GTK2 2.2 REQUIRED gtk)
include_directories(${GTK2_INCLUDE_DIRS})
target_link_libraries(jack-keyboard ${GTK2_LIBRARIES})
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)

View File

@ -67,6 +67,15 @@
#define VELOCITY_LOW 32
#define VELOCITY_MIN 1
#define MOD_MIN 0
#define MOD_MAX 127
#define MOD_INIT 0
#define PITCH_MIN -100
#define PITCH_MAX 100
#define PITCH_INIT 0
#define PITCH_RANGE 8192
#define OUTPUT_PORT_NAME "midi_out"
#define INPUT_PORT_NAME "midi_in"
#define PACKAGE_NAME "jack-keyboard"
@ -88,6 +97,7 @@ int velocity_high = VELOCITY_HIGH;
int velocity_normal = VELOCITY_NORMAL;
int velocity_low = VELOCITY_LOW;
int *current_velocity = &velocity_normal;
short alt_l = False;
int octave = 4;
double rate_limit = 0.0;
jack_client_t *jack_client = NULL;
@ -100,13 +110,15 @@ lash_client_t *lash_client;
#define MIDI_NOTE_OFF 0x80
#define MIDI_PROGRAM_CHANGE 0xC0
#define MIDI_CONTROLLER 0xB0
#define MIDI_RESET 0xFF
#define MIDI_PITCH 0xE0
#define MIDI_RESET 0xFF
#define MIDI_HOLD_PEDAL 64
#define MIDI_ALL_SOUND_OFF 120
#define MIDI_ALL_MIDI_CONTROLLERS_OFF 121
#define MIDI_ALL_NOTES_OFF 123
#define MIDI_BANK_SELECT_MSB 0
#define MIDI_BANK_SELECT_LSB 32
#define MIDI_BANK_MSB_SELECT 0
#define MIDI_BANK_LSB_SELECT 32
#define MIDI_MOD_CC 1
#define BANK_MIN 0
#define BANK_MAX 127
@ -115,8 +127,58 @@ lash_client_t *lash_client;
#define CHANNEL_MIN 1
#define CHANNEL_MAX 16
GtkWidget *window, *sustain_button, *channel_spin, *bank_spin, *program_spin, *connected_to_combo,
*velocity_hscale, *grab_keyboard_checkbutton, *octave_spin;
const char *rc_style = "style \"default\"\n"
"{\n"
" xthickness = 3\n"
" ythickness = 2\n"
" fg[NORMAL] = \"#fff\"\n"
" fg[PRELIGHT] = \"#fff\"\n"
" fg[ACTIVE] = \"#fff\"\n"
" fg[SELECTED] = \"#fff\"\n"
" fg[INSENSITIVE] = \"#aaa\"\n"
" bg[NORMAL] = \"#222\"\n"
" bg[PRELIGHT] = \"#444\"\n"
" bg[ACTIVE] = \"#222\"\n"
" bg[SELECTED] = \"#0af\"\n"
" bg[INSENSITIVE] = \"#666\"\n"
" base[NORMAL] = \"#000\"\n"
" base[PRELIGHT] = \"#444\"\n"
" base[ACTIVE] = \"#444\"\n"
" base[SELECTED] = \"#444\"\n"
" base[INSENSITIVE] = \"#333\"\n"
" text[NORMAL] = \"#fff\"\n"
" text[PRELIGHT] = \"#fff\"\n"
" text[ACTIVE] = \"#f80\"\n"
" text[SELECTED] = \"#f80\"\n"
" text[INSENSITIVE] = \"#666\"\n"
"}\n"
"style \"button\"\n"
"{\n"
" fg[NORMAL] = \"#fff\"\n"
" bg[NORMAL] = \"#000\"\n"
" bg[PRELIGHT] = \"#333\"\n"
" bg[ACTIVE] = \"#444\"\n"
" bg[INSENSITIVE] = \"#222\"\n"
"}\n"
"style \"scale\"\n"
"{\n"
" bg[NORMAL] = \"#555\"\n"
" bg[PRELIGHT] = \"#000\"\n"
" bg[ACTIVE] = \"#000\"\n"
" bg[INSENSITIVE] = \"#333\"\n"
"}\n"
"style \"label\"\n"
"{\n"
" fg[NORMAL] = \"#aaa\"\n"
"}\n"
"widget \"*\" style : rc \"default\"\n"
"widget_class\"*Gtk*Scale*\" style : highest \"scale\"\n"
"widget_class\"*GtkLabel*\" style : highest \"label\"\n"
"widget_class\"*GtkButton*\" style : highest \"button\"\n"
"widget_class\"*GtkToggle*\" style : highest \"button\"\n";
GtkWidget *window, *sustain_button, *channel_spin, *bank_msb_spin, *bank_lsb_spin, *program_spin, *connected_to_combo,
*velocity_scale, *grab_keyboard_checkbutton, *octave_spin, *mod_scale, *pitch_scale, *panic_button;
PianoKeyboard *keyboard;
GtkListStore *connected_to_store;
@ -144,7 +206,8 @@ jack_ringbuffer_t *ringbuffer;
int program = 0;
/* Number of currently selected bank. */
int bank = 0;
int bank_msb = 0;
int bank_lsb = 0;
/* Number of currently selected channel (0..15). */
int channel = 0;
@ -218,7 +281,7 @@ process_received_message_async(gpointer evp)
if (ev->data[2] == 0)
piano_keyboard_set_note_off(keyboard, ev->data[1]);
else
piano_keyboard_set_note_on(keyboard, ev->data[1]);
piano_keyboard_set_note_on(keyboard, ev->data[1], ev->data[2]);
}
if (b0 == MIDI_NOTE_OFF) {
@ -256,7 +319,7 @@ warning_async(gpointer s)
{
const char *str = (const char *)s;
g_warning(str);
g_warning("%s",str);
return (FALSE);
}
@ -537,8 +600,8 @@ draw_window_title(void)
if (enable_window_title && jack_client != NULL) {
connected_ports = jack_port_get_connections(output_port);
off += snprintf(title, sizeof(title) - off, "%s: channel %d, bank %d, program %d",
jack_get_client_name(jack_client), channel + 1, bank, program);
off += snprintf(title, sizeof(title) - off, "%s: channel %d, bank %d/%d, program %d",
jack_get_client_name(jack_client), channel + 1, bank_msb, bank_lsb, program);
if (!program_change_was_sent)
off += snprintf(title + off, sizeof(title) - off, " (bank/program change not sent)");
@ -592,8 +655,10 @@ send_program_change(void)
if (jack_port_connected(output_port) == 0)
return;
queue_new_message(MIDI_CONTROLLER, MIDI_BANK_SELECT_LSB, bank % 128);
queue_new_message(MIDI_CONTROLLER, MIDI_BANK_SELECT_MSB, bank / 128);
// queue_new_message(MIDI_CONTROLLER, MIDI_BANK_SELECT_MSB, bank % 128);
// queue_new_message(MIDI_CONTROLLER, MIDI_BANK_SELECT_LSB, bank / 128);
queue_new_message(MIDI_CONTROLLER, MIDI_BANK_MSB_SELECT, bank_msb);
queue_new_message(MIDI_CONTROLLER, MIDI_BANK_LSB_SELECT, bank_lsb);
queue_new_message(MIDI_PROGRAM_CHANGE, program, -1);
program_change_was_sent = 1;
@ -826,11 +891,18 @@ load_config_from_lash(void)
gtk_spin_button_set_value(GTK_SPIN_BUTTON(channel_spin), value);
}
} else if (!strcmp(key, "bank")) {
} else if (!strcmp(key, "bank_msb")) {
if (value < BANK_MIN || value > BANK_MAX) {
g_warning("Bad value '%d' for 'bank' property received from LASH.", value);
g_warning("Bad value '%d' for 'bank_msb' property received from LASH.", value);
} else {
gtk_spin_button_set_value(GTK_SPIN_BUTTON(bank_spin), value);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(bank_msb_spin), value);
}
} else if (!strcmp(key, "bank_lsb")) {
if (value < BANK_MIN || value > BANK_MAX) {
g_warning("Bad value '%d' for 'bank_lsb' property received from LASH.", value);
} else {
gtk_spin_button_set_value(GTK_SPIN_BUTTON(bank_lsb_spin), value);
}
} else if (!strcmp(key, "program")) {
@ -859,7 +931,7 @@ load_config_from_lash(void)
g_warning("Bad value '%d' for 'velocity_normal' property received from LASH.", value);
} else {
velocity_normal = value;
gtk_range_set_value(GTK_RANGE(velocity_hscale), *current_velocity);
gtk_range_set_value(GTK_RANGE(velocity_scale), *current_velocity);
}
} else if (!strcmp(key, "velocity_high")) {
@ -867,7 +939,7 @@ load_config_from_lash(void)
g_warning("Bad value '%d' for 'velocity_high' property received from LASH.", value);
} else {
velocity_high = value;
gtk_range_set_value(GTK_RANGE(velocity_hscale), *current_velocity);
gtk_range_set_value(GTK_RANGE(velocity_scale), *current_velocity);
}
} else if (!strcmp(key, "velocity_low")) {
@ -875,7 +947,7 @@ load_config_from_lash(void)
g_warning("Bad value '%d' for 'velocity_low' property received from LASH.", value);
} else {
velocity_low = value;
gtk_range_set_value(GTK_RANGE(velocity_hscale), *current_velocity);
gtk_range_set_value(GTK_RANGE(velocity_scale), *current_velocity);
}
} else {
@ -900,7 +972,8 @@ void
save_config_into_lash(void)
{
save_config_int("channel", channel + 1);
save_config_int("bank", bank);
save_config_int("bank_msb", bank_msb);
save_config_int("bank_lsb", bank_lsb);
save_config_int("program", program);
save_config_int("keyboard_grabbed", keyboard_grabbed);
save_config_int("octave", octave);
@ -982,9 +1055,18 @@ channel_event_handler(GtkSpinButton *spinbutton, gpointer notused)
}
void
bank_event_handler(GtkSpinButton *spinbutton, gpointer notused)
bank_msb_event_handler(GtkSpinButton *spinbutton, gpointer notused)
{
bank = gtk_spin_button_get_value(spinbutton);
bank_msb = gtk_spin_button_get_value(spinbutton);
send_program_change();
draw_window_title();
}
void
bank_lsb_event_handler(GtkSpinButton *spinbutton, gpointer notused)
{
bank_lsb = gtk_spin_button_get_value(spinbutton);
send_program_change();
draw_window_title();
@ -1028,6 +1110,27 @@ velocity_event_handler(GtkRange *range, gpointer notused)
assert(current_velocity);
*current_velocity = gtk_range_get_value(range);
keyboard->current_velocity = gtk_range_get_value(range);
}
void
mod_event_handler(GtkRange *range, gpointer notused)
{
int val = (int) gtk_range_get_value(range);
queue_new_message(MIDI_CONTROLLER, MIDI_MOD_CC, val);
}
void
pitch_event_handler(GtkRange *range, gpointer notused)
{
uint16_t val = (uint16_t) (gtk_range_get_value(range) * ((float)PITCH_RANGE / (float)PITCH_MAX) + (float)PITCH_RANGE);
queue_new_message(MIDI_PITCH, val & 127, (val >> 7) & 127);
}
void
panic_event_handler(GtkWidget *widget)
{
panic();
}
#ifdef HAVE_X11
@ -1319,6 +1422,7 @@ keyboard_event_handler(GtkWidget *widget, GdkEventKey *event, gpointer notused)
{
int tmp;
gboolean retval = FALSE;
GtkWidget *tmp_widget;
/* Pass signal to piano_keyboard widget. Is there a better way to do this? */
if (event->type == GDK_KEY_PRESS)
@ -1382,17 +1486,23 @@ keyboard_event_handler(GtkWidget *widget, GdkEventKey *event, gpointer notused)
}
/*
* PgUp key increases bank number, PgDown decreases it.
* PgUp key increases bank MSB number, PgDown decreases it.
* PgUp + Alt left key increases bank LSB number, PgDown + Alt left decreases it.
*/
if (event->keyval == GDK_Page_Up) {
if (event->type == GDK_KEY_PRESS) {
if( alt_l == False)
tmp_widget = GTK_SPIN_BUTTON(bank_msb_spin);
else
tmp_widget = GTK_SPIN_BUTTON(bank_lsb_spin);
tmp = get_entered_number();
if (tmp < 0)
tmp = gtk_spin_button_get_value(GTK_SPIN_BUTTON(bank_spin)) + 1;
tmp = gtk_spin_button_get_value(tmp_widget) + 1;
gtk_spin_button_set_value(GTK_SPIN_BUTTON(bank_spin), clip(tmp, BANK_MIN, BANK_MAX));
gtk_spin_button_set_value(tmp_widget, clip(tmp, BANK_MIN, BANK_MAX));
}
return (TRUE);
@ -1401,12 +1511,17 @@ keyboard_event_handler(GtkWidget *widget, GdkEventKey *event, gpointer notused)
if (event->keyval == GDK_Page_Down) {
if (event->type == GDK_KEY_PRESS) {
if( alt_l == False)
tmp_widget = GTK_SPIN_BUTTON(bank_msb_spin);
else
tmp_widget = GTK_SPIN_BUTTON(bank_lsb_spin);
tmp = get_entered_number();
if (tmp < 0)
tmp = gtk_spin_button_get_value(GTK_SPIN_BUTTON(bank_spin)) - 1;
tmp = gtk_spin_button_get_value(tmp_widget) - 1;
gtk_spin_button_set_value(GTK_SPIN_BUTTON(bank_spin), clip(tmp, BANK_MIN, BANK_MAX));
gtk_spin_button_set_value(tmp_widget, clip(tmp, BANK_MIN, BANK_MAX));
}
return (TRUE);
@ -1493,7 +1608,7 @@ keyboard_event_handler(GtkWidget *widget, GdkEventKey *event, gpointer notused)
else
current_velocity = &velocity_normal;
gtk_range_set_value(GTK_RANGE(velocity_hscale), *current_velocity);
gtk_range_set_value(GTK_RANGE(velocity_scale), *current_velocity);
return (TRUE);
@ -1505,7 +1620,19 @@ keyboard_event_handler(GtkWidget *widget, GdkEventKey *event, gpointer notused)
else
current_velocity = &velocity_normal;
gtk_range_set_value(GTK_RANGE(velocity_hscale), *current_velocity);
gtk_range_set_value(GTK_RANGE(velocity_scale), *current_velocity);
return (TRUE);
}
/*
* Alt Left is used as modifier key
*/
if (event->keyval == GDK_Alt_L ) {
if (event->type == GDK_KEY_PRESS)
alt_l = True;
else
alt_l = False;
return (TRUE);
}
@ -1550,54 +1677,88 @@ init_gtk_1(int *argc, char ***argv)
/* Window. */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(window), 2);
gtk_rc_parse_string (rc_style);
}
void
init_gtk_2(void)
{
GtkTable *table;
keyboard = PIANO_KEYBOARD(piano_keyboard_new());
if (!enable_gui) {
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(keyboard));
return;
}
GtkWidget *label;
GtkCellRenderer *renderer;
/* Table. */
table = GTK_TABLE(gtk_table_new(4, 8, FALSE));
gtk_table_set_row_spacings(table, 5);
gtk_table_set_col_spacings(table, 5);
if (enable_gui)
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(table));
/* Main Table. */
GtkTable *maintable = GTK_TABLE(gtk_table_new(3, 4, FALSE));
gtk_table_set_row_spacings(maintable, 5);
gtk_table_set_col_spacings(maintable, 5);
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(maintable));
/* Num Table. */
GtkTable *numtable = GTK_TABLE(gtk_table_new(2, 4, FALSE));
gtk_table_set_row_spacings(numtable, 5);
gtk_table_set_col_spacings(numtable, 5);
gtk_table_attach(maintable, numtable, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
/* Channel label and spin. */
label = gtk_label_new("Channel:");
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
gtk_table_attach(table, label, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new("Chan");
gtk_table_attach(numtable, label, 0, 1, 0, 1,GTK_FILL, GTK_FILL, 0, 0);
channel_spin = gtk_spin_button_new_with_range(1, CHANNEL_MAX, 1);
GTK_WIDGET_UNSET_FLAGS(channel_spin, GTK_CAN_FOCUS);
gtk_table_attach(table, channel_spin, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach(numtable, channel_spin, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(channel_spin), "value-changed", G_CALLBACK(channel_event_handler), NULL);
/* Octave label and spin. */
label = gtk_label_new("Oct");
gtk_table_attach(numtable, label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
octave_spin = gtk_spin_button_new_with_range(OCTAVE_MIN, OCTAVE_MAX, 1);
GTK_WIDGET_UNSET_FLAGS(octave_spin, GTK_CAN_FOCUS);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(octave_spin), octave);
gtk_table_attach(numtable, octave_spin, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(octave_spin), "value-changed", G_CALLBACK(octave_event_handler), NULL);
/* Bank MSB label and spin. */
label = gtk_label_new("Bank MSB");
gtk_table_attach(numtable, label, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
bank_msb_spin = gtk_spin_button_new_with_range(0, BANK_MAX, 1);
GTK_WIDGET_UNSET_FLAGS(bank_msb_spin, GTK_CAN_FOCUS);
gtk_table_attach(numtable, bank_msb_spin, 2, 3, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(bank_msb_spin), "value-changed", G_CALLBACK(bank_msb_event_handler), NULL);
/* Bank label and spin. */
label = gtk_label_new("Bank:");
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
gtk_table_attach(table, label, 2, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
bank_spin = gtk_spin_button_new_with_range(0, BANK_MAX, 1);
GTK_WIDGET_UNSET_FLAGS(bank_spin, GTK_CAN_FOCUS);
gtk_table_attach(table, bank_spin, 3, 4, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(bank_spin), "value-changed", G_CALLBACK(bank_event_handler), NULL);
/* Bank LSB label and spin. */
label = gtk_label_new("Bank LSB");
gtk_table_attach(numtable, label, 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
bank_lsb_spin = gtk_spin_button_new_with_range(0, BANK_MAX, 1);
GTK_WIDGET_UNSET_FLAGS(bank_lsb_spin, GTK_CAN_FOCUS);
gtk_table_attach(numtable, bank_lsb_spin, 3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(bank_lsb_spin), "value-changed", G_CALLBACK(bank_lsb_event_handler), NULL);
/* Program label and spin. */
label = gtk_label_new("Program:");
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
gtk_table_attach(table, label, 4, 5, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new("Prog");
gtk_table_attach(numtable, label, 4, 5, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
program_spin = gtk_spin_button_new_with_range(0, PROGRAM_MAX, 1);
GTK_WIDGET_UNSET_FLAGS(program_spin, GTK_CAN_FOCUS);
gtk_table_attach(table, program_spin, 5, 6, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach(numtable, program_spin, 4, 5, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(program_spin), "value-changed", G_CALLBACK(program_event_handler), NULL);
/* PANIC!!1 */
panic_button = gtk_button_new_with_label(" Panic! ");
gtk_button_set_focus_on_click(GTK_BUTTON(panic_button), FALSE);
gtk_table_attach(maintable, panic_button, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(panic_button), "pressed", G_CALLBACK(panic_event_handler), NULL);
/* "Connected to" label and combo box. */
label = gtk_label_new("Connected to:");
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
gtk_table_attach(table, label, 6, 7, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
GtkCellRenderer *renderer;
label = gtk_label_new("Connect");
gtk_table_attach(numtable, label, 5, 6, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
connected_to_store = gtk_list_store_new(1, G_TYPE_STRING);
connected_to_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(connected_to_store));
@ -1608,57 +1769,82 @@ init_gtk_2(void)
GTK_WIDGET_UNSET_FLAGS(connected_to_combo, GTK_CAN_FOCUS);
gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(connected_to_combo), FALSE);
gtk_table_attach(table, connected_to_combo, 7, 8, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_set_size_request(GTK_WIDGET(connected_to_combo), 200, -1);
gtk_table_attach(numtable, connected_to_combo, 5, 6, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_set_size_request(GTK_WIDGET(connected_to_combo), 160, -1);
g_signal_connect(G_OBJECT(connected_to_combo), "changed", G_CALLBACK(connected_to_event_handler), NULL);
/* Octave label and spin. */
label = gtk_label_new("Octave:");
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
gtk_table_attach(table, label, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
octave_spin = gtk_spin_button_new_with_range(OCTAVE_MIN, OCTAVE_MAX, 1);
GTK_WIDGET_UNSET_FLAGS(octave_spin, GTK_CAN_FOCUS);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(octave_spin), octave);
gtk_table_attach(table, octave_spin, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(octave_spin), "value-changed", G_CALLBACK(octave_event_handler), NULL);
/* "Grab keyboard" label and checkbutton. */
label = gtk_label_new("Grab keyboard:");
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
gtk_table_attach(table, label, 4, 5, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
label = gtk_label_new("Grab Keyboard");
gtk_table_attach(numtable, label, 6, 7, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
grab_keyboard_checkbutton = gtk_check_button_new();
GTK_WIDGET_UNSET_FLAGS(grab_keyboard_checkbutton, GTK_CAN_FOCUS);
g_signal_connect(G_OBJECT(grab_keyboard_checkbutton), "toggled", G_CALLBACK(grab_keyboard_handler), NULL);
gtk_table_attach(table, grab_keyboard_checkbutton, 5, 6, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach(numtable, grab_keyboard_checkbutton, 6, 7, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
/* Velocity label and hscale */
label = gtk_label_new("Velocity:");
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
gtk_table_attach(table, label, 6, 7, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
velocity_hscale = gtk_hscale_new_with_range(VELOCITY_MIN, VELOCITY_MAX, 1);
gtk_scale_set_draw_value(GTK_SCALE(velocity_hscale), FALSE);
GTK_WIDGET_UNSET_FLAGS(velocity_hscale, GTK_CAN_FOCUS);
g_signal_connect(G_OBJECT(velocity_hscale), "value-changed", G_CALLBACK(velocity_event_handler), NULL);
gtk_range_set_value(GTK_RANGE(velocity_hscale), VELOCITY_NORMAL);
gtk_table_attach(table, velocity_hscale, 7, 8, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_set_size_request(GTK_WIDGET(velocity_hscale), 200, -1);
/* Sustain. It's a toggle button, not an ordinary one, because we want gtk_whatever_set_active() to work.*/
sustain_button = gtk_toggle_button_new_with_label("Sustain");
gtk_button_set_focus_on_click(GTK_BUTTON(sustain_button), FALSE);
gtk_table_attach(table, sustain_button, 0, 8, 2, 3, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach(maintable, sustain_button, 0, 3, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(sustain_button), "pressed", G_CALLBACK(sustain_event_handler), (void *)1);
g_signal_connect(G_OBJECT(sustain_button), "released", G_CALLBACK(sustain_event_handler), (void *)0);
/* Mod Table. */
GtkTable *modtable = GTK_TABLE(gtk_table_new(2, 3, FALSE));
gtk_table_set_row_spacings(modtable, 2);
gtk_table_set_col_spacings(modtable, 5);
gtk_table_attach(maintable, modtable, 3, 4, 0, 3, GTK_FILL, GTK_FILL, 0, 0);
/* Velocity label and hscale */
label = gtk_label_new("Vel");
gtk_table_attach(modtable, label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
/* PianoKeyboard widget. */
keyboard = PIANO_KEYBOARD(piano_keyboard_new());
velocity_scale = gtk_vscale_new_with_range(VELOCITY_MIN, VELOCITY_MAX, 1);
gtk_scale_set_value_pos(GTK_SCALE(velocity_scale), GTK_POS_BOTTOM);
GTK_WIDGET_UNSET_FLAGS(velocity_scale, GTK_CAN_FOCUS);
g_signal_connect(G_OBJECT(velocity_scale), "value-changed", G_CALLBACK(velocity_event_handler), NULL);
gtk_range_set_value(GTK_RANGE(velocity_scale), VELOCITY_NORMAL);
gtk_range_set_inverted(GTK_RANGE(velocity_scale), TRUE);
gtk_range_set_round_digits(GTK_RANGE(velocity_scale), 0);
gtk_table_attach(modtable, velocity_scale, 0, 1, 1, 2, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_widget_set_size_request(GTK_WIDGET(velocity_scale), 40, 160);
/* Modulation label and vscale */
label = gtk_label_new("Mod");
gtk_table_attach(modtable, label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
if (enable_gui)
gtk_table_attach_defaults(table, GTK_WIDGET(keyboard), 0, 8, 3, 4);
else
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(keyboard));
mod_scale = gtk_vscale_new_with_range(MOD_MIN, MOD_MAX, 1);
gtk_scale_set_value_pos(GTK_SCALE(mod_scale), GTK_POS_BOTTOM);
GTK_WIDGET_UNSET_FLAGS(mod_scale, GTK_CAN_FOCUS);
gtk_range_set_value(GTK_RANGE(mod_scale), MOD_INIT);
gtk_range_set_inverted(GTK_RANGE(mod_scale), TRUE);
gtk_range_set_round_digits(GTK_RANGE(mod_scale), 0);
g_signal_connect(G_OBJECT(mod_scale), "value-changed", G_CALLBACK(mod_event_handler), NULL);
gtk_table_attach(modtable, mod_scale, 1, 2, 1, 2, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_widget_set_size_request(GTK_WIDGET(mod_scale), 40, 160);
/* Pitch Bend label and vscale */
label = gtk_label_new("Pitch");
gtk_table_attach(modtable, label, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
pitch_scale = gtk_vscale_new_with_range(PITCH_MIN, PITCH_MAX, 1);
gtk_scale_set_value_pos(GTK_SCALE(pitch_scale), GTK_POS_BOTTOM);
GTK_WIDGET_UNSET_FLAGS(pitch_scale, GTK_CAN_FOCUS);
gtk_range_set_value(GTK_RANGE(pitch_scale), PITCH_INIT);
gtk_range_set_inverted(GTK_RANGE(pitch_scale), TRUE);
gtk_range_set_round_digits(GTK_RANGE(pitch_scale), 0);
g_signal_connect(G_OBJECT(pitch_scale), "value-changed", G_CALLBACK(pitch_event_handler), NULL);
gtk_table_attach(modtable, pitch_scale, 2, 3, 1, 2, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
gtk_widget_set_size_request(GTK_WIDGET(pitch_scale), 40, 160);
/* Keyboard */
gtk_table_attach(maintable, GTK_WIDGET(keyboard), 0, 3, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(keyboard), "note-on", G_CALLBACK(note_on_event_handler), NULL);
g_signal_connect(G_OBJECT(keyboard), "note-off", G_CALLBACK(note_off_event_handler), NULL);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
@ -1677,8 +1863,8 @@ log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *mess
fprintf(stderr, "%s: %s\n", log_domain, message);
if ((log_level | G_LOG_LEVEL_CRITICAL) == G_LOG_LEVEL_CRITICAL) {
dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE, message);
dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE, "%s", message);
gtk_dialog_run(GTK_DIALOG(dialog));
@ -1697,9 +1883,9 @@ show_version(void)
void
usage(void)
{
fprintf(stderr, "usage: jack-keyboard [-CGKTVkturf] [ -a <input port>] [-c <channel>] [-b <bank> ] [-p <program>] [-l <layout>]\n");
fprintf(stderr, "usage: jack-keyboard [-CGKTVkturf] [ -a <input port>] [-c <channel>] [-b <bank msb> ] [-d <bank lsb> ] [-p <program>] [-l <layout>]\n");
fprintf(stderr, " where <channel> is MIDI channel to use for output, from 1 to 16,\n");
fprintf(stderr, " <bank> is MIDI bank to use, from 0 to 16383,\n");
fprintf(stderr, " <bank> is MIDI bank to use, from 0 to 127,\n");
fprintf(stderr, " <program> is MIDI program to use, from 0 to 127,\n");
fprintf(stderr, " and <layout> is QWERTY.\n");
fprintf(stderr, "See manual page for details.\n");
@ -1710,7 +1896,7 @@ usage(void)
int
main(int argc, char *argv[])
{
int ch, enable_keyboard_cue = 0, initial_channel = 1, initial_bank = 0, initial_program = 0, full_midi_keyboard = 0;
int ch, enable_keyboard_cue = 0, initial_channel = 1, initial_bank_msb = 0, initial_bank_lsb = 0, initial_program = 0, full_midi_keyboard = 0;
char *keyboard_layout = NULL, *autoconnect_port_name = NULL;
#ifdef HAVE_LASH
@ -1727,7 +1913,7 @@ main(int argc, char *argv[])
g_log_set_default_handler(log_handler, NULL);
while ((ch = getopt(argc, argv, "CGKTVa:nktur:c:b:p:l:f")) != -1) {
while ((ch = getopt(argc, argv, "CGKTVa:nktur:c:b:d:p:l:f")) != -1) {
switch (ch) {
case 'C':
enable_keyboard_cue = 1;
@ -1787,12 +1973,26 @@ main(int argc, char *argv[])
break;
case 'b':
initial_bank = atoi(optarg);
initial_bank_msb = atoi(optarg);
send_program_change_once = 1;
if (initial_bank < BANK_MIN || initial_bank > BANK_MAX) {
g_critical("Invalid MIDI bank number specified on the command line; "
if (initial_bank_msb < BANK_MIN || initial_bank_msb > BANK_MAX) {
g_critical("Invalid MIDI MSB bank number specified on the command line; "
"valid values are %d-%d.", BANK_MIN, BANK_MAX);
exit(EX_USAGE);
}
break;
case 'd':
initial_bank_lsb = atoi(optarg);
send_program_change_once = 1;
if (initial_bank_lsb < BANK_MIN || initial_bank_lsb > BANK_MAX) {
g_critical("Invalid MIDI LSB bank number specified on the command line; "
"valid values are %d-%d.", BANK_MIN, BANK_MAX);
exit(EX_USAGE);
@ -1866,7 +2066,8 @@ main(int argc, char *argv[])
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grab_keyboard_checkbutton), grab_keyboard_at_startup);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(channel_spin), initial_channel);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(bank_spin), initial_bank);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(bank_msb_spin), initial_bank_msb);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(bank_lsb_spin), initial_bank_lsb);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(program_spin), initial_program);
piano_keyboard_set_keyboard_cue(keyboard, enable_keyboard_cue);

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"
@ -77,17 +79,12 @@ draw_keyboard_cue(PianoKeyboard *pk)
static void
draw_note(PianoKeyboard *pk, int note)
{
GtkWidget *widget = GTK_WIDGET(pk);
if (note < pk->min_note)
return;
if (note > pk->max_note)
return;
int is_white, x, w, h;
GdkColor black = {0, 0, 0, 0};
GdkColor white = {0, 65535, 65535, 65535};
GdkGC *gc = GTK_WIDGET(pk)->style->fg_gc[0];
GtkWidget *widget;
int is_white, x, w, h, pressed;
is_white = pk->notes[note].white;
@ -95,28 +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)
gdk_gc_set_rgb_fg_color(gc, &white);
else
gdk_gc_set_rgb_fg_color(gc, &black);
gdk_draw_rectangle(GTK_WIDGET(pk)->window, gc, TRUE, x, 0, w, h);
gdk_gc_set_rgb_fg_color(gc, &black);
gdk_draw_rectangle(GTK_WIDGET(pk)->window, gc, FALSE, x, 0, w, h);
if (pk->enable_keyboard_cue)
draw_keyboard_cue(pk);
/* We need to redraw black keys that partially obscure the white one. */
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);
/*
* 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
@ -147,6 +139,7 @@ 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);
draw_note(pk, key);
@ -487,7 +480,7 @@ recompute_dimensions(PianoKeyboard *pk)
(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;
}
@ -623,10 +616,11 @@ 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;
pk->notes[note].velocity = vel;
draw_note(pk, note);
}
}
@ -673,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