# HG changeset patch # User Chris Cannam # Date 1461150388 -3600 # Node ID c02c51ae52388ddf7b51ab4d294094d4be601e15 # Parent 3691af49291c57a31883fd27190586737935b12b# Parent 282f4be8f0589a7b0744ba4ef7a838defc53ba4a Merge branches 3.0-integration and imaf_enc to 3.0-plus-imaf diff -r 282f4be8f058 -r c02c51ae5238 acinclude.m4 --- a/acinclude.m4 Tue Jul 14 15:04:46 2015 +0100 +++ b/acinclude.m4 Wed Apr 20 12:06:28 2016 +0100 @@ -69,6 +69,9 @@ AC_CHECK_PROG(QMAKE, qmake-qt5, $QTDIR/bin/qmake-qt5,,$QTDIR/bin/) fi if test x$QMAKE = x ; then + AC_CHECK_PROG(QMAKE, qt5-qmake, $QTDIR/bin/qt5-qmake,,$QTDIR/bin/) +fi +if test x$QMAKE = x ; then AC_CHECK_PROG(QMAKE, qmake, $QTDIR/bin/qmake,,$QTDIR/bin/) fi if test x$QMAKE = x ; then @@ -78,6 +81,9 @@ AC_CHECK_PROG(QMAKE, qmake-qt5, qmake-qt5,,$PATH) fi if test x$QMAKE = x ; then + AC_CHECK_PROG(QMAKE, qt5-qmake, qt5-qmake,,$PATH) +fi +if test x$QMAKE = x ; then AC_CHECK_PROG(QMAKE, qmake, qmake,,$PATH) fi if test x$QMAKE = x ; then @@ -112,3 +118,146 @@ ]) +# From autoconf archive: + +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXXFLAGS to enable support. +# +# The first argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The second argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline C++11 support is required and that the macro +# should error out if no mode with that support is found. If specified +# 'optional', then configuration proceeds regardless, after defining +# HAVE_CXX11 if and only if a supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014 Alexey Sokolov +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() override {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + auto l = [](){}; +]]) + +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl + m4_if([$1], [], [], + [$1], [ext], [], + [$1], [noext], [], + [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl + m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], + [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], + [$2], [optional], [ax_cxx_compile_cxx11_required=false], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++11 features by default, + ax_cv_cxx_compile_cxx11, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [ax_cv_cxx_compile_cxx11=yes], + [ax_cv_cxx_compile_cxx11=no])]) + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + m4_if([$1], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++11 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + + m4_if([$1], [ext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + AC_MSG_NOTICE([No compiler with C++11 support was found]) + else + HAVE_CXX11=1 + AC_DEFINE(HAVE_CXX11,1, + [define if the compiler supports basic C++11 syntax]) + fi + + AC_SUBST(HAVE_CXX11) + fi +]) + diff -r 282f4be8f058 -r c02c51ae5238 configure --- a/configure Tue Jul 14 15:04:46 2015 +0100 +++ b/configure Wed Apr 20 12:06:28 2016 +0100 @@ -646,16 +646,12 @@ libpulse_CFLAGS JACK_LIBS JACK_CFLAGS -portaudio_2_0_LIBS -portaudio_2_0_CFLAGS +portaudio_LIBS +portaudio_CFLAGS liblo_LIBS liblo_CFLAGS rubberband_LIBS rubberband_CFLAGS -vamphostsdk_LIBS -vamphostsdk_CFLAGS -vamp_LIBS -vamp_CFLAGS samplerate_LIBS samplerate_CFLAGS sndfile_LIBS @@ -673,6 +669,7 @@ EGREP GREP CXXCPP +HAVE_CXX11 MKDIR_P INSTALL_DATA INSTALL_SCRIPT @@ -755,16 +752,12 @@ sndfile_LIBS samplerate_CFLAGS samplerate_LIBS -vamp_CFLAGS -vamp_LIBS -vamphostsdk_CFLAGS -vamphostsdk_LIBS rubberband_CFLAGS rubberband_LIBS liblo_CFLAGS liblo_LIBS -portaudio_2_0_CFLAGS -portaudio_2_0_LIBS +portaudio_CFLAGS +portaudio_LIBS JACK_CFLAGS JACK_LIBS libpulse_CFLAGS @@ -1422,12 +1415,6 @@ C compiler flags for samplerate, overriding pkg-config samplerate_LIBS linker flags for samplerate, overriding pkg-config - vamp_CFLAGS C compiler flags for vamp, overriding pkg-config - vamp_LIBS linker flags for vamp, overriding pkg-config - vamphostsdk_CFLAGS - C compiler flags for vamphostsdk, overriding pkg-config - vamphostsdk_LIBS - linker flags for vamphostsdk, overriding pkg-config rubberband_CFLAGS C compiler flags for rubberband, overriding pkg-config rubberband_LIBS @@ -1435,10 +1422,10 @@ liblo_CFLAGS C compiler flags for liblo, overriding pkg-config liblo_LIBS linker flags for liblo, overriding pkg-config - portaudio_2_0_CFLAGS - C compiler flags for portaudio_2_0, overriding pkg-config - portaudio_2_0_LIBS - linker flags for portaudio_2_0, overriding pkg-config + portaudio_CFLAGS + C compiler flags for portaudio, overriding pkg-config + portaudio_LIBS + linker flags for portaudio, overriding pkg-config JACK_CFLAGS C compiler flags for JACK, overriding pkg-config JACK_LIBS linker flags for JACK, overriding pkg-config libpulse_CFLAGS @@ -3449,6 +3436,146 @@ $as_echo "$MKDIR_P" >&6; } +# We are daringly making use of C++11 now + + ax_cxx_compile_cxx11_required=true + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + ac_success=no + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 +$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } +if ${ax_cv_cxx_compile_cxx11+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() override {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + auto l = [](){}; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_cxx_compile_cxx11=yes +else + ax_cv_cxx_compile_cxx11=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 +$as_echo "$ax_cv_cxx_compile_cxx11" >&6; } + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + + + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 +$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } +if eval \${$cachevar+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() override {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + auto l = [](){}; + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval $cachevar=yes +else + eval $cachevar=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS="$ac_save_CXXFLAGS" +fi +eval ac_res=\$$cachevar + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 +$as_echo "$as_me: No compiler with C++11 support was found" >&6;} + else + HAVE_CXX11=1 + +$as_echo "#define HAVE_CXX11 1" >>confdefs.h + + fi + + + fi + ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' @@ -3996,6 +4123,45 @@ fi if test x$QMAKE = x ; then + # Extract the first word of "qt5-qmake", so it can be a program name with args. +set dummy qt5-qmake; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_QMAKE+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$QMAKE"; then + ac_cv_prog_QMAKE="$QMAKE" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $QTDIR/bin/ +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_QMAKE="$QTDIR/bin/qt5-qmake" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +QMAKE=$ac_cv_prog_QMAKE +if test -n "$QMAKE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QMAKE" >&5 +$as_echo "$QMAKE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test x$QMAKE = x ; then # Extract the first word of "qmake", so it can be a program name with args. set dummy qmake; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -4113,6 +4279,45 @@ fi if test x$QMAKE = x ; then + # Extract the first word of "qt5-qmake", so it can be a program name with args. +set dummy qt5-qmake; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_QMAKE+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$QMAKE"; then + ac_cv_prog_QMAKE="$QMAKE" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_QMAKE="qt5-qmake" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +QMAKE=$ac_cv_prog_QMAKE +if test -n "$QMAKE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QMAKE" >&5 +$as_echo "$QMAKE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test x$QMAKE = x ; then # Extract the first word of "qmake", so it can be a program name with args. set dummy qmake; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 @@ -4202,9 +4407,10 @@ CXXFLAGS_MINIMAL="$AUTOCONF_CXXFLAGS" if test "x$GCC" = "xyes"; then - CXXFLAGS_DEBUG="-Wall -Woverloaded-virtual -Wextra -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -g -pipe" - CXXFLAGS_RELEASE="-g0 -O2 -Wall -pipe" - CXXFLAGS_MINIMAL="-g0 -O0" + CXXFLAGS_ANY="-Wall -Wextra -Woverloaded-virtual -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -Wconversion -pipe" + CXXFLAGS_DEBUG="$CXXFLAGS_ANY -Werror -g" + CXXFLAGS_RELEASE="$CXXFLAGS_ANY -g0 -O2" + CXXFLAGS_MINIMAL="$CXXFLAGS_ANY -g0 -O0" fi CXXFLAGS_BUILD="$CXXFLAGS_RELEASE" @@ -5006,18 +5212,18 @@ fi -SV_MODULE_MODULE=vamp -SV_MODULE_VERSION_TEST="vamp >= 2.1" -SV_MODULE_HEADER=vamp/vamp.h -SV_MODULE_LIB= -SV_MODULE_FUNC= -SV_MODULE_HAVE=HAVE_$(echo vamp | tr 'a-z' 'A-Z') +SV_MODULE_MODULE=rubberband +SV_MODULE_VERSION_TEST="rubberband" +SV_MODULE_HEADER=rubberband/RubberBandStretcher.h +SV_MODULE_LIB=rubberband +SV_MODULE_FUNC=rubberband_new +SV_MODULE_HAVE=HAVE_$(echo rubberband | tr 'a-z' 'A-Z') SV_MODULE_FAILED=1 -if test -n "$vamp_LIBS" ; then +if test -n "$rubberband_LIBS" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5 $as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;} - CXXFLAGS="$CXXFLAGS $vamp_CFLAGS" - LIBS="$LIBS $vamp_LIBS" + CXXFLAGS="$CXXFLAGS $rubberband_CFLAGS" + LIBS="$LIBS $rubberband_LIBS" SV_MODULE_FAILED="" fi if test -z "$SV_MODULE_VERSION_TEST" ; then @@ -5026,11 +5232,11 @@ if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for vamp" >&5 -$as_echo_n "checking for vamp... " >&6; } - -if test -n "$vamp_CFLAGS"; then - pkg_cv_vamp_CFLAGS="$vamp_CFLAGS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rubberband" >&5 +$as_echo_n "checking for rubberband... " >&6; } + +if test -n "$rubberband_CFLAGS"; then + pkg_cv_rubberband_CFLAGS="$rubberband_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 @@ -5038,7 +5244,7 @@ ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_vamp_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` + pkg_cv_rubberband_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -5046,8 +5252,8 @@ else pkg_failed=untried fi -if test -n "$vamp_LIBS"; then - pkg_cv_vamp_LIBS="$vamp_LIBS" +if test -n "$rubberband_LIBS"; then + pkg_cv_rubberband_LIBS="$rubberband_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 @@ -5055,7 +5261,7 @@ ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_vamp_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` + pkg_cv_rubberband_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -5076,12 +5282,12 @@ _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - vamp_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` + rubberband_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` else - vamp_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` + rubberband_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` fi # Put the nasty error message in config.log where it belongs - echo "$vamp_PKG_ERRORS" >&5 + echo "$rubberband_PKG_ERRORS" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 $as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} @@ -5091,11 +5297,11 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 $as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} else - vamp_CFLAGS=$pkg_cv_vamp_CFLAGS - vamp_LIBS=$pkg_cv_vamp_LIBS + rubberband_CFLAGS=$pkg_cv_rubberband_CFLAGS + rubberband_LIBS=$pkg_cv_rubberband_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $vamp_CFLAGS";LIBS="$LIBS $vamp_LIBS";SV_MODULE_FAILED="" + HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $rubberband_CFLAGS";LIBS="$LIBS $rubberband_LIBS";SV_MODULE_FAILED="" fi fi if test -n "$SV_MODULE_FAILED"; then @@ -5157,18 +5363,19 @@ fi -SV_MODULE_MODULE=vamphostsdk -SV_MODULE_VERSION_TEST="vamp-hostsdk >= 2.5" -SV_MODULE_HEADER=vamp-hostsdk/PluginLoader.h -SV_MODULE_LIB=vamp-hostsdk -SV_MODULE_FUNC=libvamphostsdk_v_2_5_present -SV_MODULE_HAVE=HAVE_$(echo vamphostsdk | tr 'a-z' 'A-Z') + +SV_MODULE_MODULE=liblo +SV_MODULE_VERSION_TEST="" +SV_MODULE_HEADER=lo/lo.h +SV_MODULE_LIB=lo +SV_MODULE_FUNC=lo_address_new +SV_MODULE_HAVE=HAVE_$(echo liblo | tr 'a-z' 'A-Z') SV_MODULE_FAILED=1 -if test -n "$vamphostsdk_LIBS" ; then +if test -n "$liblo_LIBS" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5 $as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;} - CXXFLAGS="$CXXFLAGS $vamphostsdk_CFLAGS" - LIBS="$LIBS $vamphostsdk_LIBS" + CXXFLAGS="$CXXFLAGS $liblo_CFLAGS" + LIBS="$LIBS $liblo_LIBS" SV_MODULE_FAILED="" fi if test -z "$SV_MODULE_VERSION_TEST" ; then @@ -5177,11 +5384,11 @@ if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for vamphostsdk" >&5 -$as_echo_n "checking for vamphostsdk... " >&6; } - -if test -n "$vamphostsdk_CFLAGS"; then - pkg_cv_vamphostsdk_CFLAGS="$vamphostsdk_CFLAGS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for liblo" >&5 +$as_echo_n "checking for liblo... " >&6; } + +if test -n "$liblo_CFLAGS"; then + pkg_cv_liblo_CFLAGS="$liblo_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 @@ -5189,7 +5396,7 @@ ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_vamphostsdk_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` + pkg_cv_liblo_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -5197,8 +5404,8 @@ else pkg_failed=untried fi -if test -n "$vamphostsdk_LIBS"; then - pkg_cv_vamphostsdk_LIBS="$vamphostsdk_LIBS" +if test -n "$liblo_LIBS"; then + pkg_cv_liblo_LIBS="$liblo_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 @@ -5206,7 +5413,7 @@ ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_vamphostsdk_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` + pkg_cv_liblo_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -5227,40 +5434,42 @@ _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - vamphostsdk_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` + liblo_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` else - vamphostsdk_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` + liblo_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` fi # Put the nasty error message in config.log where it belongs - echo "$vamphostsdk_PKG_ERRORS" >&5 - - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} + echo "$liblo_PKG_ERRORS" >&5 + + { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 +$as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -else - vamphostsdk_CFLAGS=$pkg_cv_vamphostsdk_CFLAGS - vamphostsdk_LIBS=$pkg_cv_vamphostsdk_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 +$as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} +else + liblo_CFLAGS=$pkg_cv_liblo_CFLAGS + liblo_LIBS=$pkg_cv_liblo_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $vamphostsdk_CFLAGS";LIBS="$LIBS $vamphostsdk_LIBS";SV_MODULE_FAILED="" + HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $liblo_CFLAGS";LIBS="$LIBS $liblo_LIBS";SV_MODULE_FAILED="" fi fi if test -n "$SV_MODULE_FAILED"; then as_ac_Header=`$as_echo "ac_cv_header_$SV_MODULE_HEADER" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "$SV_MODULE_HEADER" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - HAVES="$HAVES $SV_MODULE_HAVE" -else - as_fn_error $? "Failed to find header $SV_MODULE_HEADER for required module $SV_MODULE_MODULE" "$LINENO" 5 -fi - - - if test -n "$SV_MODULE_LIB"; then - as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh` + HAVES="$HAVES $SV_MODULE_HAVE";SV_MODULE_FAILED="" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find header $SV_MODULE_HEADER for optional module $SV_MODULE_MODULE" >&5 +$as_echo "$as_me: Failed to find header $SV_MODULE_HEADER for optional module $SV_MODULE_MODULE" >&6;} +fi + + + if test -z "$SV_MODULE_FAILED"; then + if test -n "$SV_MODULE_LIB"; then + as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB" >&5 $as_echo_n "checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB... " >&6; } if eval \${$as_ac_Lib+:} false; then : @@ -5301,25 +5510,27 @@ if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : LIBS="$LIBS -l$SV_MODULE_LIB" else - as_fn_error $? "Failed to find library $SV_MODULE_LIB for required module $SV_MODULE_MODULE" "$LINENO" 5 -fi - + { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find library $SV_MODULE_LIB for optional module $SV_MODULE_MODULE" >&5 +$as_echo "$as_me: Failed to find library $SV_MODULE_LIB for optional module $SV_MODULE_MODULE" >&6;} +fi + + fi fi fi -SV_MODULE_MODULE=rubberband -SV_MODULE_VERSION_TEST="rubberband" -SV_MODULE_HEADER=rubberband/RubberBandStretcher.h -SV_MODULE_LIB=rubberband -SV_MODULE_FUNC=rubberband_new -SV_MODULE_HAVE=HAVE_$(echo rubberband | tr 'a-z' 'A-Z') +SV_MODULE_MODULE=portaudio +SV_MODULE_VERSION_TEST="portaudio-2.0 >= 19" +SV_MODULE_HEADER=portaudio.h +SV_MODULE_LIB=portaudio +SV_MODULE_FUNC=Pa_IsFormatSupported +SV_MODULE_HAVE=HAVE_$(echo portaudio | tr 'a-z' 'A-Z') SV_MODULE_FAILED=1 -if test -n "$rubberband_LIBS" ; then +if test -n "$portaudio_LIBS" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5 $as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;} - CXXFLAGS="$CXXFLAGS $rubberband_CFLAGS" - LIBS="$LIBS $rubberband_LIBS" + CXXFLAGS="$CXXFLAGS $portaudio_CFLAGS" + LIBS="$LIBS $portaudio_LIBS" SV_MODULE_FAILED="" fi if test -z "$SV_MODULE_VERSION_TEST" ; then @@ -5328,11 +5539,11 @@ if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rubberband" >&5 -$as_echo_n "checking for rubberband... " >&6; } - -if test -n "$rubberband_CFLAGS"; then - pkg_cv_rubberband_CFLAGS="$rubberband_CFLAGS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for portaudio" >&5 +$as_echo_n "checking for portaudio... " >&6; } + +if test -n "$portaudio_CFLAGS"; then + pkg_cv_portaudio_CFLAGS="$portaudio_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 @@ -5340,7 +5551,7 @@ ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_rubberband_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` + pkg_cv_portaudio_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -5348,8 +5559,8 @@ else pkg_failed=untried fi -if test -n "$rubberband_LIBS"; then - pkg_cv_rubberband_LIBS="$rubberband_LIBS" +if test -n "$portaudio_LIBS"; then + pkg_cv_portaudio_LIBS="$portaudio_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 @@ -5357,7 +5568,7 @@ ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_rubberband_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` + pkg_cv_portaudio_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -5378,164 +5589,12 @@ _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - rubberband_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` + portaudio_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` else - rubberband_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` + portaudio_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` fi # Put the nasty error message in config.log where it belongs - echo "$rubberband_PKG_ERRORS" >&5 - - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -else - rubberband_CFLAGS=$pkg_cv_rubberband_CFLAGS - rubberband_LIBS=$pkg_cv_rubberband_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $rubberband_CFLAGS";LIBS="$LIBS $rubberband_LIBS";SV_MODULE_FAILED="" -fi -fi -if test -n "$SV_MODULE_FAILED"; then - as_ac_Header=`$as_echo "ac_cv_header_$SV_MODULE_HEADER" | $as_tr_sh` -ac_fn_cxx_check_header_mongrel "$LINENO" "$SV_MODULE_HEADER" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - HAVES="$HAVES $SV_MODULE_HAVE" -else - as_fn_error $? "Failed to find header $SV_MODULE_HEADER for required module $SV_MODULE_MODULE" "$LINENO" 5 -fi - - - if test -n "$SV_MODULE_LIB"; then - as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB" >&5 -$as_echo_n "checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$SV_MODULE_LIB $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $SV_MODULE_FUNC (); -int -main () -{ -return $SV_MODULE_FUNC (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - LIBS="$LIBS -l$SV_MODULE_LIB" -else - as_fn_error $? "Failed to find library $SV_MODULE_LIB for required module $SV_MODULE_MODULE" "$LINENO" 5 -fi - - fi -fi - - - -SV_MODULE_MODULE=liblo -SV_MODULE_VERSION_TEST="" -SV_MODULE_HEADER=lo/lo.h -SV_MODULE_LIB=lo -SV_MODULE_FUNC=lo_address_new -SV_MODULE_HAVE=HAVE_$(echo liblo | tr 'a-z' 'A-Z') -SV_MODULE_FAILED=1 -if test -n "$liblo_LIBS" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5 -$as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;} - CXXFLAGS="$CXXFLAGS $liblo_CFLAGS" - LIBS="$LIBS $liblo_LIBS" - SV_MODULE_FAILED="" -fi -if test -z "$SV_MODULE_VERSION_TEST" ; then - SV_MODULE_VERSION_TEST=$SV_MODULE_MODULE -fi -if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for liblo" >&5 -$as_echo_n "checking for liblo... " >&6; } - -if test -n "$liblo_CFLAGS"; then - pkg_cv_liblo_CFLAGS="$liblo_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_liblo_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$liblo_LIBS"; then - pkg_cv_liblo_LIBS="$liblo_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_liblo_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - liblo_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` - else - liblo_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$liblo_PKG_ERRORS" >&5 + echo "$portaudio_PKG_ERRORS" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 $as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} @@ -5545,166 +5604,11 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 $as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} else - liblo_CFLAGS=$pkg_cv_liblo_CFLAGS - liblo_LIBS=$pkg_cv_liblo_LIBS + portaudio_CFLAGS=$pkg_cv_portaudio_CFLAGS + portaudio_LIBS=$pkg_cv_portaudio_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $liblo_CFLAGS";LIBS="$LIBS $liblo_LIBS";SV_MODULE_FAILED="" -fi -fi -if test -n "$SV_MODULE_FAILED"; then - as_ac_Header=`$as_echo "ac_cv_header_$SV_MODULE_HEADER" | $as_tr_sh` -ac_fn_cxx_check_header_mongrel "$LINENO" "$SV_MODULE_HEADER" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - HAVES="$HAVES $SV_MODULE_HAVE";SV_MODULE_FAILED="" -else - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find header $SV_MODULE_HEADER for optional module $SV_MODULE_MODULE" >&5 -$as_echo "$as_me: Failed to find header $SV_MODULE_HEADER for optional module $SV_MODULE_MODULE" >&6;} -fi - - - if test -z "$SV_MODULE_FAILED"; then - if test -n "$SV_MODULE_LIB"; then - as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB" >&5 -$as_echo_n "checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$SV_MODULE_LIB $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $SV_MODULE_FUNC (); -int -main () -{ -return $SV_MODULE_FUNC (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - LIBS="$LIBS -l$SV_MODULE_LIB" -else - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find library $SV_MODULE_LIB for optional module $SV_MODULE_MODULE" >&5 -$as_echo "$as_me: Failed to find library $SV_MODULE_LIB for optional module $SV_MODULE_MODULE" >&6;} -fi - - fi - fi -fi - - -SV_MODULE_MODULE=portaudio_2_0 -SV_MODULE_VERSION_TEST="portaudio-2.0 >= 19" -SV_MODULE_HEADER=portaudio.h -SV_MODULE_LIB=portaudio -SV_MODULE_FUNC=Pa_IsFormatSupported -SV_MODULE_HAVE=HAVE_$(echo portaudio_2_0 | tr 'a-z' 'A-Z') -SV_MODULE_FAILED=1 -if test -n "$portaudio_2_0_LIBS" ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5 -$as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;} - CXXFLAGS="$CXXFLAGS $portaudio_2_0_CFLAGS" - LIBS="$LIBS $portaudio_2_0_LIBS" - SV_MODULE_FAILED="" -fi -if test -z "$SV_MODULE_VERSION_TEST" ; then - SV_MODULE_VERSION_TEST=$SV_MODULE_MODULE -fi -if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then - -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for portaudio_2_0" >&5 -$as_echo_n "checking for portaudio_2_0... " >&6; } - -if test -n "$portaudio_2_0_CFLAGS"; then - pkg_cv_portaudio_2_0_CFLAGS="$portaudio_2_0_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_portaudio_2_0_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$portaudio_2_0_LIBS"; then - pkg_cv_portaudio_2_0_LIBS="$portaudio_2_0_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_portaudio_2_0_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - portaudio_2_0_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` - else - portaudio_2_0_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$portaudio_2_0_PKG_ERRORS" >&5 - - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5 -$as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;} -else - portaudio_2_0_CFLAGS=$pkg_cv_portaudio_2_0_CFLAGS - portaudio_2_0_LIBS=$pkg_cv_portaudio_2_0_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $portaudio_2_0_CFLAGS";LIBS="$LIBS $portaudio_2_0_LIBS";SV_MODULE_FAILED="" + HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $portaudio_CFLAGS";LIBS="$LIBS $portaudio_LIBS";SV_MODULE_FAILED="" fi fi if test -n "$SV_MODULE_FAILED"; then diff -r 282f4be8f058 -r c02c51ae5238 configure.ac --- a/configure.ac Tue Jul 14 15:04:46 2015 +0100 +++ b/configure.ac Wed Apr 20 12:06:28 2016 +0100 @@ -25,6 +25,9 @@ AC_PROG_INSTALL AC_PROG_MKDIR_P +# We are daringly making use of C++11 now +AX_CXX_COMPILE_STDCXX_11(noext) + AC_HEADER_STDC # These are the flags Autoconf guesses for us; we use them later if @@ -50,9 +53,10 @@ CXXFLAGS_MINIMAL="$AUTOCONF_CXXFLAGS" if test "x$GCC" = "xyes"; then - CXXFLAGS_DEBUG="-Wall -Woverloaded-virtual -Wextra -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -g -pipe" - CXXFLAGS_RELEASE="-g0 -O2 -Wall -pipe" - CXXFLAGS_MINIMAL="-g0 -O0" + CXXFLAGS_ANY="-Wall -Wextra -Woverloaded-virtual -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -Wconversion -pipe" + CXXFLAGS_DEBUG="$CXXFLAGS_ANY -Werror -g" + CXXFLAGS_RELEASE="$CXXFLAGS_ANY -g0 -O2" + CXXFLAGS_MINIMAL="$CXXFLAGS_ANY -g0 -O0" fi CXXFLAGS_BUILD="$CXXFLAGS_RELEASE" @@ -79,12 +83,10 @@ SV_MODULE_REQUIRED([fftw3f],[fftw3f >= 3.0.0],[fftw3.h],[fftw3f],[fftwf_execute]) SV_MODULE_REQUIRED([sndfile],[sndfile >= 1.0.16],[sndfile.h],[sndfile],[sf_open]) SV_MODULE_REQUIRED([samplerate],[samplerate >= 0.1.2],[samplerate.h],[samplerate],[src_new]) -SV_MODULE_REQUIRED([vamp],[vamp >= 2.1],[vamp/vamp.h],[],[]) -SV_MODULE_REQUIRED([vamphostsdk],[vamp-hostsdk >= 2.5],[vamp-hostsdk/PluginLoader.h],[vamp-hostsdk],[libvamphostsdk_v_2_5_present]) SV_MODULE_REQUIRED([rubberband],[rubberband],[rubberband/RubberBandStretcher.h],[rubberband],[rubberband_new]) SV_MODULE_OPTIONAL([liblo],[],[lo/lo.h],[lo],[lo_address_new]) -SV_MODULE_OPTIONAL([portaudio_2_0],[portaudio-2.0 >= 19],[portaudio.h],[portaudio],[Pa_IsFormatSupported]) +SV_MODULE_OPTIONAL([portaudio],[portaudio-2.0 >= 19],[portaudio.h],[portaudio],[Pa_IsFormatSupported]) SV_MODULE_OPTIONAL([JACK],[jack >= 0.100],[jack/jack.h],[jack],[jack_client_open]) SV_MODULE_OPTIONAL([libpulse],[libpulse >= 0.9],[pulse/pulseaudio.h],[pulse],[pa_stream_new]) SV_MODULE_OPTIONAL([lrdf],[lrdf >= 0.2],[lrdf.h],[lrdf],[lrdf_init]) diff -r 282f4be8f058 -r c02c51ae5238 layer/Colour3DPlotLayer.cpp --- a/layer/Colour3DPlotLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/Colour3DPlotLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -34,6 +35,8 @@ #include #endif +using std::vector; + //#define DEBUG_COLOUR_3D_PLOT_LAYER_PAINT 1 @@ -41,7 +44,6 @@ m_model(0), m_cache(0), m_peaksCache(0), - m_peakResolution(256), m_cacheValidStart(0), m_cacheValidEnd(0), m_colourScale(LinearScale), @@ -51,13 +53,18 @@ m_binScale(LinearBinScale), m_normalizeColumns(false), m_normalizeVisibleArea(false), + m_normalizeHybrid(false), m_invertVertical(false), m_opaque(false), m_smooth(false), + m_peakResolution(256), m_miny(0), m_maxy(0) { - + QSettings settings; + settings.beginGroup("Preferences"); + setColourMap(settings.value("colour-3d-plot-colour", ColourMapper::Green).toInt()); + settings.endGroup(); } Colour3DPlotLayer::~Colour3DPlotLayer() @@ -77,8 +84,8 @@ connectSignals(m_model); connect(m_model, SIGNAL(modelChanged()), this, SLOT(modelChanged())); - connect(m_model, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(modelChanged(size_t, size_t))); + connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), + this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); m_peakResolution = 256; if (model->getResolution() > 512) { @@ -106,13 +113,13 @@ } void -Colour3DPlotLayer::cacheInvalid(size_t startFrame, size_t endFrame) +Colour3DPlotLayer::cacheInvalid(sv_frame_t startFrame, sv_frame_t endFrame) { - if (!m_cache) return; + if (!m_cache || !m_model) return; - size_t modelResolution = m_model->getResolution(); - size_t start = startFrame / modelResolution; - size_t end = endFrame / modelResolution + 1; + int modelResolution = m_model->getResolution(); + int start = int(startFrame / modelResolution); + int end = int(endFrame / modelResolution + 1); if (m_cacheValidStart < end) m_cacheValidStart = end; if (m_cacheValidEnd > start) m_cacheValidEnd = start; if (m_cacheValidStart > m_cacheValidEnd) m_cacheValidEnd = m_cacheValidStart; @@ -134,7 +141,7 @@ } void -Colour3DPlotLayer::modelChanged(size_t startFrame, size_t endFrame) +Colour3DPlotLayer::modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame) { if (!m_colourScaleSet && m_colourScale == LinearScale) { if (m_model && m_model->getWidth() > 50) { @@ -233,11 +240,11 @@ *min = -50; *max = 50; - *deflt = lrintf(log10(1.f) * 20.0);; + *deflt = int(lrint(log10(1.0) * 20.0)); if (*deflt < *min) *deflt = *min; if (*deflt > *max) *deflt = *max; - val = lrintf(log10(m_gain) * 20.0); + val = int(lrint(log10(m_gain) * 20.0)); if (val < *min) val = *min; if (val > *max) val = *max; @@ -335,7 +342,7 @@ Colour3DPlotLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(pow(10, float(value)/20.0)); + setGain(float(pow(10, value/20.0))); } else if (name == "Colour Scale") { switch (value) { default: @@ -430,6 +437,21 @@ } void +Colour3DPlotLayer::setNormalizeHybrid(bool n) +{ + if (m_normalizeHybrid == n) return; + m_normalizeHybrid = n; + cacheInvalid(); + emit layerParametersChanged(); +} + +bool +Colour3DPlotLayer::getNormalizeHybrid() const +{ + return m_normalizeHybrid; +} + +void Colour3DPlotLayer::setNormalizeVisibleArea(bool n) { if (m_normalizeVisibleArea == n) return; @@ -488,12 +510,12 @@ } void -Colour3DPlotLayer::setLayerDormant(const View *v, bool dormant) +Colour3DPlotLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant) { if (dormant) { #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT - SVDEBUG << "Colour3DPlotLayer::setLayerDormant(" << dormant << ")" + cerr << "Colour3DPlotLayer::setLayerDormant(" << dormant << ")" << endl; #endif @@ -512,21 +534,26 @@ } bool -Colour3DPlotLayer::isLayerScrollable(const View *v) const +Colour3DPlotLayer::isLayerScrollable(const LayerGeometryProvider *v) const { - if (m_normalizeVisibleArea) return false; + if (m_normalizeVisibleArea) { + return false; + } + if (shouldPaintDenseIn(v)) { + return true; + } QPoint discard; return !v->shouldIlluminateLocalFeatures(this, discard); } bool -Colour3DPlotLayer::getValueExtents(float &min, float &max, +Colour3DPlotLayer::getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const { if (!m_model) return false; min = 0; - max = m_model->getHeight(); + max = double(m_model->getHeight()); logarithmic = false; unit = ""; @@ -535,34 +562,43 @@ } bool -Colour3DPlotLayer::getDisplayExtents(float &min, float &max) const +Colour3DPlotLayer::getDisplayExtents(double &min, double &max) const { if (!m_model) return false; + double hmax = double(m_model->getHeight()); + min = m_miny; max = m_maxy; if (max <= min) { min = 0; - max = m_model->getHeight(); + max = hmax; } if (min < 0) min = 0; - if (max > m_model->getHeight()) max = m_model->getHeight(); + if (max > hmax) max = hmax; return true; } bool -Colour3DPlotLayer::setDisplayExtents(float min, float max) +Colour3DPlotLayer::setDisplayExtents(double min, double max) { if (!m_model) return false; - m_miny = lrintf(min); - m_maxy = lrintf(max); + m_miny = int(lrint(min)); + m_maxy = int(lrint(max)); emit layerParametersChanged(); return true; } +bool +Colour3DPlotLayer::getYScaleValue(const LayerGeometryProvider *, int, + double &, QString &) const +{ + return false;//!!! +} + int Colour3DPlotLayer::getVerticalZoomSteps(int &defaultStep) const { @@ -578,9 +614,9 @@ { if (!m_model) return 0; - float min, max; + double min, max; getDisplayExtents(min, max); - return m_model->getHeight() - lrintf(max - min); + return m_model->getHeight() - int(lrint(max - min)); } void @@ -592,8 +628,8 @@ int dist = m_model->getHeight() - step; if (dist < 1) dist = 1; - float centre = m_miny + (float(m_maxy) - float(m_miny)) / 2.f; - m_miny = lrintf(centre - float(dist)/2); + double centre = m_miny + (m_maxy - m_miny) / 2.0; + m_miny = int(lrint(centre - dist/2.0)); if (m_miny < 0) m_miny = 0; m_maxy = m_miny + dist; if (m_maxy > m_model->getHeight()) m_maxy = m_model->getHeight(); @@ -612,59 +648,71 @@ 0, m_model->getHeight(), ""); } -float -Colour3DPlotLayer::getYForBin(View *v, float bin) const +double +Colour3DPlotLayer::getYForBin(LayerGeometryProvider *v, double bin) const { - float y = bin; + double y = bin; if (!m_model) return y; - float mn = 0, mx = m_model->getHeight(); + double mn = 0, mx = m_model->getHeight(); getDisplayExtents(mn, mx); - float h = v->height(); + double h = v->getPaintHeight(); if (m_binScale == LinearBinScale) { y = h - (((bin - mn) * h) / (mx - mn)); } else { - float logmin = mn + 1, logmax = mx + 1; + double logmin = mn + 1, logmax = mx + 1; LogRange::mapRange(logmin, logmax); y = h - (((LogRange::map(bin + 1) - logmin) * h) / (logmax - logmin)); } return y; } -float -Colour3DPlotLayer::getBinForY(View *v, float y) const +int +Colour3DPlotLayer::getIYForBin(LayerGeometryProvider *v, int bin) const { - float bin = y; + return int(round(getYForBin(v, bin))); +} + +double +Colour3DPlotLayer::getBinForY(LayerGeometryProvider *v, double y) const +{ + double bin = y; if (!m_model) return bin; - float mn = 0, mx = m_model->getHeight(); + double mn = 0, mx = m_model->getHeight(); getDisplayExtents(mn, mx); - float h = v->height(); + double h = v->getPaintHeight(); if (m_binScale == LinearBinScale) { bin = mn + ((h - y) * (mx - mn)) / h; } else { - float logmin = mn + 1, logmax = mx + 1; + double logmin = mn + 1, logmax = mx + 1; LogRange::mapRange(logmin, logmax); bin = LogRange::unmap(logmin + ((h - y) * (logmax - logmin)) / h) - 1; } return bin; } +int +Colour3DPlotLayer::getIBinForY(LayerGeometryProvider *v, int y) const +{ + return int(floor(getBinForY(v, y))); +} + QString -Colour3DPlotLayer::getFeatureDescription(View *v, QPoint &pos) const +Colour3DPlotLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { if (!m_model) return ""; int x = pos.x(); int y = pos.y(); - size_t modelStart = m_model->getStartFrame(); - size_t modelResolution = m_model->getResolution(); + sv_frame_t modelStart = m_model->getStartFrame(); + int modelResolution = m_model->getResolution(); - float srRatio = - float(v->getViewManager()->getMainModelSampleRate()) / - float(m_model->getSampleRate()); + double srRatio = + v->getViewManager()->getMainModelSampleRate() / + m_model->getSampleRate(); - int sx0 = int((v->getFrameForX(x) / srRatio - long(modelStart)) / - long(modelResolution)); + int sx0 = int((double(v->getFrameForX(x)) / srRatio - double(modelStart)) / + modelResolution); int f0 = sx0 * modelResolution; int f1 = f0 + modelResolution; @@ -680,10 +728,14 @@ if (symin < 0) symin = 0; if (symax > sh) symax = sh; - // float binHeight = float(v->height()) / (symax - symin); -// int sy = int((v->height() - y) / binHeight) + symin; + // double binHeight = double(v->getPaintHeight()) / (symax - symin); +// int sy = int((v->getPaintHeight() - y) / binHeight) + symin; - int sy = getBinForY(v, y); + int sy = getIBinForY(v, y); + + if (sy < 0 || sy >= m_model->getHeight()) { + return ""; + } if (m_invertVertical) sy = m_model->getHeight() - sy - 1; @@ -707,14 +759,15 @@ } int -Colour3DPlotLayer::getColourScaleWidth(QPainter &) const +Colour3DPlotLayer::getColourScaleWidth(QPainter &p) const { - int cw = 20; + // Font is rotated + int cw = p.fontMetrics().height(); return cw; } int -Colour3DPlotLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const +Colour3DPlotLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const { if (!m_model) return 0; @@ -722,7 +775,7 @@ int tw = paint.fontMetrics().width(sampleText); bool another = false; - for (size_t i = 0; i < m_model->getHeight(); ++i) { + for (int i = 0; i < m_model->getHeight(); ++i) { if (m_model->getBinName(i).length() > sampleText.length()) { sampleText = m_model->getBinName(i); another = true; @@ -736,7 +789,7 @@ } void -Colour3DPlotLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const +Colour3DPlotLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const { if (!m_model) return; @@ -747,11 +800,11 @@ int ch = h - 20; if (ch > 20 && m_cache) { - float min = m_model->getMinimumLevel(); - float max = m_model->getMaximumLevel(); + double min = m_model->getMinimumLevel(); + double max = m_model->getMaximumLevel(); - float mmin = min; - float mmax = max; + double mmin = min; + double mmax = max; if (m_colourScale == LogScale) { LogRange::mapRange(mmin, mmax); @@ -760,23 +813,23 @@ mmax = 1.f; } else if (m_colourScale == AbsoluteScale) { if (mmin < 0) { - if (fabsf(mmin) > fabsf(mmax)) mmax = fabsf(mmin); - else mmax = fabsf(mmax); + if (fabs(mmin) > fabs(mmax)) mmax = fabs(mmin); + else mmax = fabs(mmax); mmin = 0; } else { - mmin = fabsf(mmin); - mmax = fabsf(mmax); + mmin = fabs(mmin); + mmax = fabs(mmax); } } - if (max == min) max = min + 1.0; - if (mmax == mmin) mmax = mmin + 1.0; + if (max == min) max = min + 1.f; + if (mmax == mmin) mmax = mmin + 1.f; paint.setPen(v->getForeground()); paint.drawRect(4, 10, cw - 8, ch+1); for (int y = 0; y < ch; ++y) { - float value = ((max - min) * (ch - y - 1)) / ch + min; + double value = ((max - min) * (double(ch-y) - 1.0)) / double(ch) + min; if (m_colourScale == LogScale) { value = LogRange::map(value); } @@ -796,7 +849,7 @@ paint.save(); QFont font = paint.font(); - font.setPixelSize(10); + font.setPixelSize(int(font.pixelSize() * 0.65)); paint.setFont(font); int msw = paint.fontMetrics().width(maxstr); @@ -834,18 +887,20 @@ int py = h; - for (size_t i = symin; i <= symax; ++i) { + int defaultFontHeight = paint.fontMetrics().height(); + + for (int i = symin; i <= symax; ++i) { int y0; - y0 = lrintf(getYForBin(v, i)); + y0 = getIYForBin(v, i); int h = py - y0; if (i > symin) { if (paint.fontMetrics().height() >= h) { - if (h >= 8) { + if (h >= defaultFontHeight * 0.8) { QFont tf = paint.font(); - tf.setPixelSize(h-2); + tf.setPixelSize(int(h * 0.8)); paint.setFont(tf); } else { continue; @@ -861,7 +916,7 @@ if (i > symin) { - size_t idx = i - 1; + int idx = i - 1; if (m_invertVertical) idx = m_model->getHeight() - idx - 1; QString text = m_model->getBinName(idx); @@ -878,64 +933,85 @@ } DenseThreeDimensionalModel::Column -Colour3DPlotLayer::getColumn(size_t col) const +Colour3DPlotLayer::getColumn(int col) const { + Profiler profiler("Colour3DPlotLayer::getColumn"); + DenseThreeDimensionalModel::Column values = m_model->getColumn(col); - while (values.size() < m_model->getHeight()) values.push_back(0.f); - if (!m_normalizeColumns) return values; + values.resize(m_model->getHeight(), 0.f); + if (!m_normalizeColumns && !m_normalizeHybrid) return values; - float colMax = 0.f, colMin = 0.f; - float min = 0.f, max = 0.f; + double colMax = 0.f, colMin = 0.f; + double min = 0.f, max = 0.f; + int nv = int(values.size()); + min = m_model->getMinimumLevel(); max = m_model->getMaximumLevel(); - for (size_t y = 0; y < values.size(); ++y) { + for (int y = 0; y < nv; ++y) { if (y == 0 || values.at(y) > colMax) colMax = values.at(y); if (y == 0 || values.at(y) < colMin) colMin = values.at(y); } if (colMin == colMax) colMax = colMin + 1; - for (size_t y = 0; y < values.size(); ++y) { + for (int y = 0; y < nv; ++y) { - float value = values.at(y); - float norm = (value - colMin) / (colMax - colMin); - float newvalue = min + (max - min) * norm; + double value = values.at(y); + double norm = (value - colMin) / (colMax - colMin); + double newvalue = min + (max - min) * norm; - if (value != newvalue) values[y] = newvalue; + if (value != newvalue) values[y] = float(newvalue); + } + + if (m_normalizeHybrid && (colMax > 0.0)) { + double logmax = log10(colMax); + for (int y = 0; y < nv; ++y) { + values[y] = float(values[y] * logmax); + } } return values; } void -Colour3DPlotLayer::fillCache(size_t firstBin, size_t lastBin) const +Colour3DPlotLayer::fillCache(int firstColumn, int lastColumn) const { - Profiler profiler("Colour3DPlotLayer::fillCache"); + // This call requests a (perhaps partial) fill of the cache + // between model columns firstColumn and lastColumn inclusive. + // The cache itself always has size sufficient to contain the + // whole model, but its validity may be less, depending on which + // regions have been requested via calls to this function. Note + // that firstColumn and lastColumn are *model* column numbers. If + // the model starts at a frame > 0, a firstColumn of zero still + // corresponds to the first column in the model, not the first + // column on the resulting rendered layer. - size_t modelStart = m_model->getStartFrame(); - size_t modelEnd = m_model->getEndFrame(); - size_t modelResolution = m_model->getResolution(); + Profiler profiler("Colour3DPlotLayer::fillCache", true); + + int cacheWidth = m_model->getWidth(); + int cacheHeight = m_model->getHeight(); #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT - SVDEBUG << "Colour3DPlotLayer::fillCache: " << firstBin << " -> " << lastBin << endl; + cerr << "Colour3DPlotLayer::fillCache: range " << firstColumn << " -> " << lastColumn << " (cache size will be " << cacheWidth << " x " << cacheHeight << ")" << endl; #endif - size_t modelStartBin = modelStart / modelResolution; - size_t modelEndBin = modelEnd / modelResolution; - - size_t cacheWidth = modelEndBin - modelStartBin + 1; - if (lastBin > modelEndBin) cacheWidth = lastBin - modelStartBin + 1; - size_t cacheHeight = m_model->getHeight(); - - if (m_cache && (m_cache->height() != int(cacheHeight))) { + if (m_cache && m_cache->height() != cacheHeight) { + // height has changed: delete everything rather than resizing +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "Colour3DPlotLayer::fillCache: Cache height has changed, recreating" << endl; +#endif delete m_cache; delete m_peaksCache; m_cache = 0; m_peaksCache = 0; } - if (m_cache && (m_cache->width() != int(cacheWidth))) { + if (m_cache && m_cache->width() != cacheWidth) { + // width has changed and we have an existing cache: resize it +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "Colour3DPlotLayer::fillCache: Cache width has changed, resizing existing cache" << endl; +#endif QImage *newCache = new QImage(m_cache->copy(0, 0, cacheWidth, cacheHeight)); delete m_cache; @@ -943,22 +1019,24 @@ if (m_peaksCache) { QImage *newPeaksCache = new QImage(m_peaksCache->copy - (0, 0, cacheWidth / m_peakResolution, cacheHeight)); + (0, 0, cacheWidth / m_peakResolution + 1, cacheHeight)); delete m_peaksCache; m_peaksCache = newPeaksCache; } } if (!m_cache) { - m_cache = new QImage - (cacheWidth, cacheHeight, QImage::Format_Indexed8); -// No longer exists in Qt5: m_cache->setNumColors(256); +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "Colour3DPlotLayer::fillCache: Have no cache, making one" << endl; +#endif + m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8); + m_cache->setColorCount(256); m_cache->fill(0); if (!m_normalizeVisibleArea) { m_peaksCache = new QImage (cacheWidth / m_peakResolution + 1, cacheHeight, QImage::Format_Indexed8); -// No longer exists in Qt5: m_peaksCache->setNumColors(256); + m_peaksCache->setColorCount(256); m_peaksCache->fill(0); } else if (m_peaksCache) { delete m_peaksCache; @@ -968,20 +1046,26 @@ m_cacheValidEnd = 0; } - if (m_cacheValidStart <= firstBin && m_cacheValidEnd >= lastBin) { +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "cache size = " << m_cache->width() << "x" << m_cache->height() + << " peaks cache size = " << m_peaksCache->width() << "x" << m_peaksCache->height() << endl; +#endif + + if (m_cacheValidStart <= firstColumn && m_cacheValidEnd >= lastColumn) { #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT cerr << "Cache is valid in this region already" << endl; #endif return; } - size_t fillStart = firstBin; - size_t fillEnd = lastBin; + int fillStart = firstColumn; + int fillEnd = lastColumn; - if (fillStart < modelStartBin) fillStart = modelStartBin; - if (fillStart > modelEndBin) fillStart = modelEndBin; - if (fillEnd < modelStartBin) fillEnd = modelStartBin; - if (fillEnd > modelEndBin) fillEnd = modelEndBin; + if (fillStart >= cacheWidth) fillStart = cacheWidth-1; + if (fillStart < 0) fillStart = 0; + if (fillEnd >= cacheWidth) fillEnd = cacheWidth-1; + if (fillEnd < 0) fillEnd = 0; + if (fillEnd < fillStart) fillEnd = fillStart; bool normalizeVisible = (m_normalizeVisibleArea && !m_normalizeColumns); @@ -999,20 +1083,21 @@ } else { - // the only valid area, ever, is the currently visible one + // when normalising the visible area, the only valid area, + // ever, is the currently visible one m_cacheValidStart = fillStart; m_cacheValidEnd = fillEnd; } #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT - cerr << "Cache size " << cacheWidth << "x" << cacheHeight << " will be valid from " << m_cacheValidStart << " to " << m_cacheValidEnd << endl; + cerr << "Cache size " << cacheWidth << "x" << cacheHeight << " will be valid from " << m_cacheValidStart << " to " << m_cacheValidEnd << " (fillStart = " << fillStart << ", fillEnd = " << fillEnd << ")" << endl; #endif DenseThreeDimensionalModel::Column values; - float min = m_model->getMinimumLevel(); - float max = m_model->getMaximumLevel(); + double min = m_model->getMinimumLevel(); + double max = m_model->getMaximumLevel(); if (m_colourScale == LogScale) { LogRange::mapRange(min, max); @@ -1021,16 +1106,16 @@ max = 1.f; } else if (m_colourScale == AbsoluteScale) { if (min < 0) { - if (fabsf(min) > fabsf(max)) max = fabsf(min); - else max = fabsf(max); + if (fabs(min) > fabs(max)) max = fabs(min); + else max = fabs(max); min = 0; } else { - min = fabsf(min); - max = fabsf(max); + min = fabs(min); + max = fabs(max); } } - if (max == min) max = min + 1.0; + if (max == min) max = min + 1.f; ColourMapper mapper(m_colourMap, 0.f, 255.f); @@ -1044,18 +1129,18 @@ } } - float visibleMax = 0.f, visibleMin = 0.f; + double visibleMax = 0.f, visibleMin = 0.f; if (normalizeVisible) { - for (size_t c = fillStart; c <= fillEnd; ++c) { + for (int c = fillStart; c <= fillEnd; ++c) { values = getColumn(c); - float colMax = 0.f, colMin = 0.f; + double colMax = 0.f, colMin = 0.f; - for (size_t y = 0; y < cacheHeight; ++y) { - if (y >= values.size()) break; + for (int y = 0; y < cacheHeight; ++y) { + if (!in_range_for(values, y)) break; if (y == 0 || values[y] > colMax) colMax = values[y]; if (y == 0 || values[y] < colMin) colMin = values[y]; } @@ -1070,12 +1155,12 @@ if (visibleMin > visibleMax) std::swap(visibleMin, visibleMax); } else if (m_colourScale == AbsoluteScale) { if (visibleMin < 0) { - if (fabsf(visibleMin) > fabsf(visibleMax)) visibleMax = fabsf(visibleMin); - else visibleMax = fabsf(visibleMax); + if (fabs(visibleMin) > fabs(visibleMax)) visibleMax = fabs(visibleMin); + else visibleMax = fabs(visibleMax); visibleMin = 0; } else { - visibleMin = fabsf(visibleMin); - visibleMax = fabsf(visibleMax); + visibleMin = fabs(visibleMin); + visibleMax = fabs(visibleMax); } } } @@ -1090,14 +1175,22 @@ } } - for (size_t c = fillStart; c <= fillEnd; ++c) { + Profiler profiler2("Colour3DPlotLayer::fillCache: filling", true); + + for (int c = fillStart; c <= fillEnd; ++c) { values = getColumn(c); - for (size_t y = 0; y < cacheHeight; ++y) { + if (c >= m_cache->width()) { + cerr << "ERROR: column " << c << " >= cache width " + << m_cache->width() << endl; + continue; + } - float value = min; - if (y < values.size()) { + for (int y = 0; y < cacheHeight; ++y) { + + double value = min; + if (in_range_for(values, y)) { value = values.at(y); } @@ -1106,11 +1199,11 @@ if (m_colourScale == LogScale) { value = LogRange::map(value); } else if (m_colourScale == AbsoluteScale) { - value = fabsf(value); + value = fabs(value); } if (normalizeVisible) { - float norm = (value - visibleMin) / (visibleMax - visibleMin); + double norm = (value - visibleMin) / (visibleMax - visibleMin); value = min + (max - min) * norm; } @@ -1122,19 +1215,35 @@ if (m_invertVertical) { m_cache->setPixel(c, cacheHeight - y - 1, pixel); } else { - m_cache->setPixel(c, y, pixel); + if (y >= m_cache->height()) { + cerr << "ERROR: row " << y << " >= cache height " << m_cache->height() << endl; + } else { + m_cache->setPixel(c, y, pixel); + } } } if (peaks) { - size_t notch = (c % m_peakResolution); + int notch = (c % m_peakResolution); if (notch == m_peakResolution-1 || c == fillEnd) { - size_t pc = c / m_peakResolution; - for (size_t y = 0; y < cacheHeight; ++y) { + int pc = c / m_peakResolution; + if (pc >= m_peaksCache->width()) { + cerr << "ERROR: peak column " << pc + << " (from col " << c << ") >= peaks cache width " + << m_peaksCache->width() << endl; + continue; + } + for (int y = 0; y < cacheHeight; ++y) { if (m_invertVertical) { m_peaksCache->setPixel(pc, cacheHeight - y - 1, peaks[y]); } else { - m_peaksCache->setPixel(pc, y, peaks[y]); + if (y >= m_peaksCache->height()) { + cerr << "ERROR: row " << y + << " >= peaks cache height " + << m_peaksCache->height() << endl; + } else { + m_peaksCache->setPixel(pc, y, peaks[y]); + } } } for (int y = 0; y < cacheHeight; ++y) { @@ -1147,8 +1256,25 @@ delete[] peaks; } +bool +Colour3DPlotLayer::shouldPaintDenseIn(const LayerGeometryProvider *v) const +{ + if (!m_model || !v || !(v->getViewManager())) { + return false; + } + double srRatio = + v->getViewManager()->getMainModelSampleRate() / m_model->getSampleRate(); + if (m_opaque || + m_smooth || + m_model->getHeight() >= v->getPaintHeight() || + ((m_model->getResolution() * srRatio) / v->getZoomLevel()) < 2) { + return true; + } + return false; +} + void -Colour3DPlotLayer::paint(View *v, QPainter &paint, QRect rect) const +Colour3DPlotLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { /* if (m_model) { @@ -1157,23 +1283,23 @@ */ Profiler profiler("Colour3DPlotLayer::paint"); #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT - SVDEBUG << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl; + cerr << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", rect is (" << rect.x() << "," << rect.y() << ") " << rect.width() << "x" << rect.height() << endl; #endif int completion = 0; if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) { if (completion > 0) { - paint.fillRect(0, 10, v->width() * completion / 100, + paint.fillRect(0, 10, v->getPaintWidth() * completion / 100, 10, QColor(120, 120, 120)); } return; } - if (m_normalizeVisibleArea && !m_normalizeColumns) rect = v->rect(); + if (m_normalizeVisibleArea && !m_normalizeColumns) rect = v->getPaintRect(); - size_t modelStart = m_model->getStartFrame(); - size_t modelEnd = m_model->getEndFrame(); - size_t modelResolution = m_model->getResolution(); + sv_frame_t modelStart = m_model->getStartFrame(); + sv_frame_t modelEnd = m_model->getEndFrame(); + int modelResolution = m_model->getResolution(); // The cache is from the model's start frame to the model's end // frame at the model's window increment frames per pixel. We @@ -1187,16 +1313,16 @@ int x0 = rect.left(); int x1 = rect.right() + 1; - int h = v->height(); + int h = v->getPaintHeight(); - float srRatio = - float(v->getViewManager()->getMainModelSampleRate()) / - float(m_model->getSampleRate()); + double srRatio = + v->getViewManager()->getMainModelSampleRate() / m_model->getSampleRate(); - int sx0 = int((v->getFrameForX(x0) / srRatio - long(modelStart)) - / long(modelResolution)); - int sx1 = int((v->getFrameForX(x1) / srRatio - long(modelStart)) - / long(modelResolution)); + // the s-prefix values are source, i.e. model, column and bin numbers + int sx0 = int((double(v->getFrameForX(x0)) / srRatio - double(modelStart)) + / modelResolution); + int sx1 = int((double(v->getFrameForX(x1)) / srRatio - double(modelStart)) + / modelResolution); int sh = m_model->getHeight(); int symin = m_miny; @@ -1213,38 +1339,36 @@ sx1 < 0 ? 0 : sx1); #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT - SVDEBUG << "Colour3DPlotLayer::paint: height = "<< m_model->getHeight() << ", modelStart = " << modelStart << ", resolution = " << modelResolution << ", model rate = " << m_model->getSampleRate() << " (zoom level = " << v->getZoomLevel() << ", srRatio = " << srRatio << ")" << endl; + cerr << "Colour3DPlotLayer::paint: height = "<< m_model->getHeight() << ", modelStart = " << modelStart << ", resolution = " << modelResolution << ", model rate = " << m_model->getSampleRate() << " (zoom level = " << v->getZoomLevel() << ", srRatio = " << srRatio << ")" << endl; #endif - if (m_opaque || - m_smooth || - int(m_model->getHeight()) >= v->height() || - ((modelResolution * srRatio) / v->getZoomLevel()) < 2) { + if (shouldPaintDenseIn(v)) { #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT - SVDEBUG << "calling paintDense" << endl; + cerr << "calling paintDense" << endl; #endif paintDense(v, paint, rect); return; } #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT - SVDEBUG << "Colour3DPlotLayer::paint: w " << x1-x0 << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sx1-sx0 << ", sh " << sh << endl; + cerr << "Colour3DPlotLayer::paint: w " << x1-x0 << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sx1-sx0 << ", sh " << sh << endl; cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", resolution " << m_model->getResolution() << endl; #endif QPoint illuminatePos; bool illuminate = v->shouldIlluminateLocalFeatures(this, illuminatePos); - char labelbuf[10]; + + const int buflen = 40; + char labelbuf[buflen]; for (int sx = sx0; sx <= sx1; ++sx) { - int fx = sx * int(modelResolution); + sv_frame_t fx = sx * modelResolution + modelStart; - if (fx + int(modelResolution) <= int(modelStart) || - fx > int(modelEnd)) continue; + if (fx + modelResolution <= modelStart || fx > modelEnd) continue; - int rx0 = v->getXForFrame(int((fx + int(modelStart)) * srRatio)); - int rx1 = v->getXForFrame(int((fx + int(modelStart) + int(modelResolution) + 1) * srRatio)); + int rx0 = v->getXForFrame(int(double(fx) * srRatio)); + int rx1 = v->getXForFrame(int(double(fx + modelResolution + 1) * srRatio)); int rw = rx1 - rx0; if (rw < 1) rw = 1; @@ -1255,8 +1379,8 @@ for (int sy = symin; sy < symax; ++sy) { - int ry0 = getYForBin(v, sy); - int ry1 = getYForBin(v, sy + 1); + int ry0 = getIYForBin(v, sy); + int ry1 = getIYForBin(v, sy + 1); QRect r(rx0, ry1, rw, ry0 - ry1); QRgb pixel = qRgb(255, 255, 255); @@ -1298,13 +1422,15 @@ if (showLabel) { if (sx >= 0 && sx < m_cache->width() && sy >= 0 && sy < m_cache->height()) { - float value = m_model->getValueAt(sx, sy); - sprintf(labelbuf, "%06f", value); + double value = m_model->getValueAt(sx, sy); + snprintf(labelbuf, buflen, "%06f", value); QString text(labelbuf); - paint.setPen(v->getBackground()); - paint.drawText(rx0 + 2, - ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(), - text); + v->drawVisibleText + (paint, + rx0 + 2, + ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(), + text, + View::OutlinedText); } } } @@ -1312,23 +1438,23 @@ } void -Colour3DPlotLayer::paintDense(View *v, QPainter &paint, QRect rect) const +Colour3DPlotLayer::paintDense(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { - Profiler profiler("Colour3DPlotLayer::paintDense"); + Profiler profiler("Colour3DPlotLayer::paintDense", true); if (!m_cache) return; - float modelStart = m_model->getStartFrame(); - float modelResolution = m_model->getResolution(); + double modelStart = double(m_model->getStartFrame()); + double modelResolution = double(m_model->getResolution()); - int mmsr = v->getViewManager()->getMainModelSampleRate(); - int msr = m_model->getSampleRate(); - float srRatio = float(mmsr) / float(msr); + sv_samplerate_t mmsr = v->getViewManager()->getMainModelSampleRate(); + sv_samplerate_t msr = m_model->getSampleRate(); + double srRatio = mmsr / msr; int x0 = rect.left(); int x1 = rect.right() + 1; const int w = x1 - x0; // const so it can be used as array size below - int h = v->height(); // we always paint full height + int h = v->getPaintHeight(); // we always paint full height int sh = m_model->getHeight(); int symin = m_miny; @@ -1349,58 +1475,66 @@ int zoomLevel = v->getZoomLevel(); QImage *source = m_cache; - - SVDEBUG << "modelResolution " << modelResolution << ", srRatio " - << srRatio << ", m_peakResolution " << m_peakResolution - << ", zoomLevel " << zoomLevel << ", result " - << ((modelResolution * srRatio * m_peakResolution) / zoomLevel) - << endl; + +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "modelResolution " << modelResolution << ", srRatio " + << srRatio << ", m_peakResolution " << m_peakResolution + << ", zoomLevel " << zoomLevel << ", result " + << ((modelResolution * srRatio * m_peakResolution) / zoomLevel) + << endl; +#endif if (m_peaksCache) { if (((modelResolution * srRatio * m_peakResolution) / zoomLevel) < 1) { - SVDEBUG << "using peaks cache" << endl; +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "using peaks cache" << endl; +#endif source = m_peaksCache; modelResolution *= m_peakResolution; } else { - SVDEBUG << "not using peaks cache" << endl; +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "not using peaks cache" << endl; +#endif } } else { - SVDEBUG << "have no peaks cache" << endl; +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "have no peaks cache" << endl; +#endif } - int psy1i = -1; int sw = source->width(); - long xf = -1; - long nxf = v->getFrameForX(x0); + sv_frame_t xf = -1; + sv_frame_t nxf = v->getFrameForX(x0); - float epsilon = 0.000001; + double epsilon = 0.000001; -#ifdef __GNUC__ - float sxa[w * 2]; -#else - float *sxa = (float *)alloca(w * 2 * sizeof(float)); -#endif + vector sxa(w*2); + for (int x = 0; x < w; ++x) { xf = nxf; nxf = xf + zoomLevel; - float sx0 = (float(xf) / srRatio - modelStart) / modelResolution; - float sx1 = (float(nxf) / srRatio - modelStart) / modelResolution; + double sx0 = (double(xf) / srRatio - modelStart) / modelResolution; + double sx1 = (double(nxf) / srRatio - modelStart) / modelResolution; sxa[x*2] = sx0; sxa[x*2 + 1] = sx1; } - float logmin = symin+1, logmax = symax+1; + double logmin = symin+1, logmax = symax+1; LogRange::mapRange(logmin, logmax); +#ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT + cerr << "m_smooth = " << m_smooth << ", w = " << w << ", h = " << h << endl; +#endif + if (m_smooth) { for (int y = 0; y < h; ++y) { - float sy = getBinForY(v, y) - 0.5; + double sy = getBinForY(v, y) - 0.5; int syi = int(sy + epsilon); if (syi < 0 || syi >= source->height()) continue; @@ -1417,50 +1551,52 @@ targetLine[x] = 0; - float sx0 = sxa[x*2]; + double sx0 = sxa[x*2]; if (sx0 < 0) continue; int sx0i = int(sx0 + epsilon); if (sx0i >= sw) break; - float a, b, value; + double a = sourceLine[sx0i]; + double b = a; + double value; - float sx1 = sxa[x*2+1]; + double sx1 = sxa[x*2+1]; if (sx1 > sx0 + 1.f) { int sx1i = int(sx1); bool have = false; for (int sx = sx0i; sx <= sx1i; ++sx) { if (sx < 0 || sx >= sw) continue; if (!have) { - a = float(sourceLine[sx]); - b = float(nextSource[sx]); + a = sourceLine[sx]; + b = nextSource[sx]; have = true; } else { - a = std::max(a, float(sourceLine[sx])); - b = std::max(b, float(nextSource[sx])); + a = std::max(a, double(sourceLine[sx])); + b = std::max(b, double(nextSource[sx])); } } - float yprop = sy - syi; + double yprop = sy - syi; value = (a * (1.f - yprop) + b * yprop); } else { - a = float(sourceLine[sx0i]); - b = float(nextSource[sx0i]); - float yprop = sy - syi; + a = sourceLine[sx0i]; + b = nextSource[sx0i]; + double yprop = sy - syi; value = (a * (1.f - yprop) + b * yprop); int oi = sx0i + 1; - float xprop = sx0 - sx0i; + double xprop = sx0 - sx0i; xprop -= 0.5; if (xprop < 0) { oi = sx0i - 1; xprop = -xprop; } if (oi < 0 || oi >= sw) oi = sx0i; - a = float(sourceLine[oi]); - b = float(nextSource[oi]); + a = sourceLine[oi]; + b = nextSource[oi]; value = (value * (1.f - xprop) + (a * (1.f - yprop) + b * yprop) * xprop); } - int vi = lrintf(value); + int vi = int(lrint(value)); if (vi > 255) vi = 255; if (vi < 0) vi = 0; targetLine[x] = uchar(vi); @@ -1468,22 +1604,28 @@ } } else { + double sy0 = getBinForY(v, 0); + + int psy0i = -1, psy1i = -1; + for (int y = 0; y < h; ++y) { - float sy0, sy1; - - sy0 = getBinForY(v, y + 1); - sy1 = getBinForY(v, y); + double sy1 = sy0; + sy0 = getBinForY(v, double(y + 1)); int sy0i = int(sy0 + epsilon); int sy1i = int(sy1); uchar *targetLine = img.scanLine(y); - if (sy0i == sy1i && sy0i == psy1i) { // same source scan line as just computed + if (sy0i == psy0i && sy1i == psy1i) { + // same source scan line as just computed goto copy; } + psy0i = sy0i; + psy1i = sy1i; + for (int x = 0; x < w; ++x) { peaks[x] = 0; } @@ -1496,11 +1638,11 @@ for (int x = 0; x < w; ++x) { - float sx1 = sxa[x*2 + 1]; + double sx1 = sxa[x*2 + 1]; if (sx1 < 0) continue; int sx1i = int(sx1); - float sx0 = sxa[x*2]; + double sx0 = sxa[x*2]; if (sx0 < 0) continue; int sx0i = int(sx0 + epsilon); if (sx0i >= sw) break; @@ -1527,8 +1669,8 @@ } bool -Colour3DPlotLayer::snapToFeatureFrame(View *v, int &frame, - size_t &resolution, +Colour3DPlotLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -1536,8 +1678,8 @@ } resolution = m_model->getResolution(); - int left = (frame / resolution) * resolution; - int right = left + resolution; + sv_frame_t left = (frame / resolution) * resolution; + sv_frame_t right = left + resolution; switch (snap) { case SnapLeft: frame = left; break; diff -r 282f4be8f058 -r c02c51ae5238 layer/Colour3DPlotLayer.h --- a/layer/Colour3DPlotLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/Colour3DPlotLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -49,20 +49,20 @@ return m_model ? m_model->getZoomConstraint() : 0; } virtual const Model *getModel() const { return m_model; } - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const; - virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const; + virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const; + virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; - virtual bool snapToFeatureFrame(View *v, int &frame, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual void setLayerDormant(const View *v, bool dormant); + virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant); - virtual bool isLayerScrollable(const View *v) const; + virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; virtual ColourSignificance getLayerColourSignificance() const { return ColourHasMeaningfulValue; @@ -70,7 +70,7 @@ void setModel(const DenseThreeDimensionalModel *model); - virtual int getCompletion(View *) const { return m_model->getCompletion(); } + virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); } virtual PropertyList getProperties() const; virtual PropertyType getPropertyType(const PropertyName &) const; @@ -116,12 +116,26 @@ void setBinScale(BinScale); BinScale getBinScale() const; + /** + * Normalize each column to its maximum value, independent of its + * neighbours. + */ void setNormalizeColumns(bool n); bool getNormalizeColumns() const; + /** + * Normalize each value against the maximum in the visible region. + */ void setNormalizeVisibleArea(bool n); bool getNormalizeVisibleArea() const; + /** + * Normalize each column to its maximum value, and then scale by + * the log of the (absolute) maximum value. + */ + void setNormalizeHybrid(bool n); + bool getNormalizeHybrid() const; + void setInvertVertical(bool i); bool getInvertVertical() const; @@ -131,11 +145,14 @@ void setSmooth(bool i); bool getSmooth() const; - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const; - virtual bool getDisplayExtents(float &min, float &max) const; - virtual bool setDisplayExtents(float min, float max); + virtual bool getDisplayExtents(double &min, double &max) const; + virtual bool setDisplayExtents(double min, double max); + + virtual bool getYScaleValue(const LayerGeometryProvider *, int /* y */, + double &/* value */, QString &/* unit */) const; virtual int getVerticalZoomSteps(int &defaultStep) const; virtual int getCurrentVerticalZoomStep() const; @@ -149,17 +166,17 @@ protected slots: void cacheInvalid(); - void cacheInvalid(size_t startFrame, size_t endFrame); + void cacheInvalid(sv_frame_t startFrame, sv_frame_t endFrame); void modelChanged(); - void modelChanged(size_t, size_t); + void modelChangedWithin(sv_frame_t, sv_frame_t); protected: const DenseThreeDimensionalModel *m_model; // I do not own this mutable QImage *m_cache; mutable QImage *m_peaksCache; - mutable size_t m_cacheValidStart; - mutable size_t m_cacheValidEnd; + mutable int m_cacheValidStart; + mutable int m_cacheValidEnd; ColourScale m_colourScale; bool m_colourScaleSet; @@ -168,22 +185,55 @@ BinScale m_binScale; bool m_normalizeColumns; bool m_normalizeVisibleArea; + bool m_normalizeHybrid; bool m_invertVertical; bool m_opaque; bool m_smooth; - size_t m_peakResolution; + int m_peakResolution; + // Minimum and maximum bin numbers visible within the view. We + // always snap to whole bins at view edges. int m_miny; int m_maxy; + + /** + * Return the y coordinate at which the given bin "starts" + * (i.e. at the bottom of the bin, if the given bin is an integer + * and the vertical scale is the usual way up). Bin number may be + * fractional, to obtain a position part-way through a bin. + */ + double getYForBin(LayerGeometryProvider *, double bin) const; + + /** + * As getYForBin, but rounding to integer values. + */ + int getIYForBin(LayerGeometryProvider *, int bin) const; - DenseThreeDimensionalModel::Column getColumn(size_t col) const; + /** + * Return the bin number, possibly fractional, at the given y + * coordinate. Note that the whole numbers occur at the positions + * at which the bins "start" (i.e. the bottom of the visible bin, + * if the vertical scale is the usual way up). + */ + double getBinForY(LayerGeometryProvider *, double y) const; + + /** + * As getBinForY, but rounding to integer values. + */ + int getIBinForY(LayerGeometryProvider *, int y) const; + + DenseThreeDimensionalModel::Column getColumn(int col) const; + + /** + * True if we have the opaque or smooth flag set, or if the cells + * are so small you can't see their borders. False for big, + * translucent cells. + */ + bool shouldPaintDenseIn(const LayerGeometryProvider *) const; int getColourScaleWidth(QPainter &) const; - void fillCache(size_t firstBin, size_t lastBin) const; - void paintDense(View *v, QPainter &paint, QRect rect) const; - - float getYForBin(View *, float bin) const; - float getBinForY(View *, float y) const; + void fillCache(int firstBin, int lastBin) const; + void paintDense(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/ColourDatabase.cpp --- a/layer/ColourDatabase.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/ColourDatabase.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -34,29 +34,28 @@ int ColourDatabase::getColourCount() const { - return m_colours.size(); + return int(m_colours.size()); } QString ColourDatabase::getColourName(int c) const { - if (c < 0 || size_t(c) >= m_colours.size()) return ""; + if (!in_range_for(m_colours, c)) return ""; return m_colours[c].name; } QColor ColourDatabase::getColour(int c) const { - if (c < 0 || size_t(c) >= m_colours.size()) return Qt::black; + if (!in_range_for(m_colours, c)) return Qt::black; return m_colours[c].colour; } QColor ColourDatabase::getColour(QString name) const { - for (ColourList::const_iterator i = m_colours.begin(); - i != m_colours.end(); ++i) { - if (i->name == name) return i->colour; + for (auto &c: m_colours) { + if (c.name == name) return c.colour; } return Qt::black; @@ -66,9 +65,8 @@ ColourDatabase::getColourIndex(QString name) const { int index = 0; - for (ColourList::const_iterator i = m_colours.begin(); - i != m_colours.end(); ++i) { - if (i->name == name) return index; + for (auto &c: m_colours) { + if (c.name == name) return index; ++index; } @@ -76,12 +74,11 @@ } int -ColourDatabase::getColourIndex(QColor c) const +ColourDatabase::getColourIndex(QColor col) const { int index = 0; - for (ColourList::const_iterator i = m_colours.begin(); - i != m_colours.end(); ++i) { - if (i->colour == c) return index; + for (auto &c: m_colours) { + if (c.colour == col) return index; ++index; } @@ -91,14 +88,14 @@ bool ColourDatabase::useDarkBackground(int c) const { - if (c < 0 || size_t(c) >= m_colours.size()) return false; + if (!in_range_for(m_colours, c)) return false; return m_colours[c].darkbg; } void ColourDatabase::setUseDarkBackground(int c, bool dark) { - if (c < 0 || size_t(c) >= m_colours.size()) return; + if (!in_range_for(m_colours, c)) return; if (m_colours[c].darkbg != dark) { m_colours[c].darkbg = dark; emit colourDatabaseChanged(); @@ -109,6 +106,7 @@ ColourDatabase::addColour(QColor c, QString name) { int index = 0; + for (ColourList::iterator i = m_colours.begin(); i != m_colours.end(); ++i) { if (i->name == name) { @@ -147,7 +145,7 @@ { colourName = ""; colourSpec = ""; - if (index < 0 || size_t(index) >= m_colours.size()) return; + if (!in_range_for(m_colours, index)) return; colourName = getColourName(index); QColor c = getColour(index); diff -r 282f4be8f058 -r c02c51ae5238 layer/ColourMapper.cpp --- a/layer/ColourMapper.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/ColourMapper.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -21,7 +21,45 @@ #include "base/Debug.h" -ColourMapper::ColourMapper(int map, float min, float max) : +#include + +using namespace std; + +static vector convertStrings(const vector &strs) +{ + vector converted; + for (const auto &s: strs) converted.push_back(QColor(s)); + reverse(converted.begin(), converted.end()); + return converted; +} + +static vector ice = convertStrings({ + // Based on ColorBrewer ylGnBu + "#ffffff", "#ffff00", "#f7fcf0", "#e0f3db", "#ccebc5", "#a8ddb5", + "#7bccc4", "#4eb3d3", "#2b8cbe", "#0868ac", "#084081", "#042040" + }); + +static vector cherry = convertStrings({ + "#f7f7f7", "#fddbc7", "#f4a582", "#d6604d", "#b2182b", "#dd3497", + "#ae017e", "#7a0177", "#49006a" + }); + +static void +mapDiscrete(double norm, vector &colours, double &r, double &g, double &b) +{ + int n = int(colours.size()); + double m = norm * (n-1); + if (m >= n-1) { colours[n-1].getRgbF(&r, &g, &b, 0); return; } + if (m <= 0) { colours[0].getRgbF(&r, &g, &b, 0); return; } + int base(int(floor(m))); + double prop0 = (base + 1.0) - m, prop1 = m - base; + QColor c0(colours[base]), c1(colours[base+1]); + r = c0.redF() * prop0 + c1.redF() * prop1; + g = c0.greenF() * prop0 + c1.greenF() * prop1; + b = c0.blueF() * prop0 + c1.blueF() * prop1; +} + +ColourMapper::ColourMapper(int map, double min, double max) : QObject(), m_map(map), m_min(min), @@ -51,12 +89,12 @@ StandardMap map = (StandardMap)n; switch (map) { - case DefaultColours: return tr("Default"); + case Green: return tr("Green"); case WhiteOnBlack: return tr("White on Black"); case BlackOnWhite: return tr("Black on White"); - case RedOnBlue: return tr("Red on Blue"); - case YellowOnBlack: return tr("Yellow on Black"); - case BlueOnBlack: return tr("Blue on Black"); + case Cherry: return tr("Cherry"); + case Wasp: return tr("Wasp"); + case Ice: return tr("Ice"); case Sunset: return tr("Sunset"); case FruitSalad: return tr("Fruit Salad"); case Banded: return tr("Banded"); @@ -69,26 +107,25 @@ } QColor -ColourMapper::map(float value) const +ColourMapper::map(double value) const { - float norm = (value - m_min) / (m_max - m_min); - if (norm < 0.f) norm = 0.f; - if (norm > 1.f) norm = 1.f; + double norm = (value - m_min) / (m_max - m_min); + if (norm < 0.0) norm = 0.0; + if (norm > 1.0) norm = 1.0; - float h = 0.f, s = 0.f, v = 0.f, r = 0.f, g = 0.f, b = 0.f; + double h = 0.0, s = 0.0, v = 0.0, r = 0.0, g = 0.0, b = 0.0; bool hsv = true; -// float red = 0.f, green = 0.3333f; - float blue = 0.6666f, pieslice = 0.3333f; + double blue = 0.6666, pieslice = 0.3333; if (m_map >= getColourMapCount()) return Qt::black; StandardMap map = (StandardMap)m_map; switch (map) { - case DefaultColours: - h = blue - norm * 2.f * pieslice; - s = 0.5f + norm/2.f; + case Green: + h = blue - norm * 2.0 * pieslice; + s = 0.5f + norm/2.0; v = norm; break; @@ -98,53 +135,40 @@ break; case BlackOnWhite: - r = g = b = 1.f - norm; + r = g = b = 1.0 - norm; hsv = false; break; - case RedOnBlue: - h = blue - pieslice/4.f + norm * (pieslice + pieslice/4.f); - s = 1.f; + case Cherry: + hsv = false; + mapDiscrete(norm, cherry, r, g, b); + break; + + case Wasp: + h = 0.15; + s = 1.0; v = norm; break; - case YellowOnBlack: - h = 0.15f; - s = 1.f; - v = norm; - break; - - case BlueOnBlack: - h = blue; - s = 1.f; - v = norm * 2.f; - if (v > 1.f) { - v = 1.f; - s = 1.f - (sqrtf(norm) - 0.707f) * 3.413f; - if (s < 0.f) s = 0.f; - if (s > 1.f) s = 1.f; - } - break; - case Sunset: - r = (norm - 0.24f) * 2.38f; - if (r > 1.f) r = 1.f; - if (r < 0.f) r = 0.f; - g = (norm - 0.64f) * 2.777f; - if (g > 1.f) g = 1.f; - if (g < 0.f) g = 0.f; + r = (norm - 0.24) * 2.38; + if (r > 1.0) r = 1.0; + if (r < 0.0) r = 0.0; + g = (norm - 0.64) * 2.777; + if (g > 1.0) g = 1.0; + if (g < 0.0) g = 0.0; b = (3.6f * norm); - if (norm > 0.277f) b = 2.f - b; - if (b > 1.f) b = 1.f; - if (b < 0.f) b = 0.f; + if (norm > 0.277) b = 2.0 - b; + if (b > 1.0) b = 1.0; + if (b < 0.0) b = 0.0; hsv = false; break; case FruitSalad: - h = blue + (pieslice/6.f) - norm; - if (h < 0.f) h += 1.f; - s = 1.f; - v = 1.f; + h = blue + (pieslice/6.0) - norm; + if (h < 0.0) h += 1.0; + s = 1.0; + v = 1.0; break; case Banded: @@ -164,50 +188,54 @@ case Printer: if (norm > 0.8) { - r = 1.f; + r = 1.0; } else if (norm > 0.7) { - r = 0.9f; + r = 0.9; } else if (norm > 0.6) { - r = 0.8f; + r = 0.8; } else if (norm > 0.5) { - r = 0.7f; + r = 0.7; } else if (norm > 0.4) { - r = 0.6f; + r = 0.6; } else if (norm > 0.3) { - r = 0.5f; + r = 0.5; } else if (norm > 0.2) { - r = 0.4f; + r = 0.4; } else { - r = 0.f; + r = 0.0; } - r = g = b = 1.f - r; + r = g = b = 1.0 - r; hsv = false; break; case HighGain: - if (norm <= 1.f / 256.f) { - norm = 0.f; + if (norm <= 1.0 / 256.0) { + norm = 0.0; } else { - norm = 0.1f + (powf(((norm - 0.5f) * 2.f), 3.f) + 1.f) / 2.081f; + norm = 0.1f + (pow(((norm - 0.5) * 2.0), 3.0) + 1.0) / 2.081; } // now as for Sunset - r = (norm - 0.24f) * 2.38f; - if (r > 1.f) r = 1.f; - if (r < 0.f) r = 0.f; - g = (norm - 0.64f) * 2.777f; - if (g > 1.f) g = 1.f; - if (g < 0.f) g = 0.f; + r = (norm - 0.24) * 2.38; + if (r > 1.0) r = 1.0; + if (r < 0.0) r = 0.0; + g = (norm - 0.64) * 2.777; + if (g > 1.0) g = 1.0; + if (g < 0.0) g = 0.0; b = (3.6f * norm); - if (norm > 0.277f) b = 2.f - b; - if (b > 1.f) b = 1.f; - if (b < 0.f) b = 0.f; + if (norm > 0.277) b = 2.0 - b; + if (b > 1.0) b = 1.0; + if (b < 0.0) b = 0.0; hsv = false; /* - if (r > 1.f) r = 1.f; - r = g = b = 1.f - r; + if (r > 1.0) r = 1.0; + r = g = b = 1.0 - r; hsv = false; */ break; + + case Ice: + hsv = false; + mapDiscrete(norm, ice, r, g, b); } if (hsv) { @@ -225,7 +253,7 @@ switch (map) { - case DefaultColours: + case Green: return QColor(255, 150, 50); case WhiteOnBlack: @@ -234,13 +262,13 @@ case BlackOnWhite: return Qt::darkGreen; - case RedOnBlue: + case Cherry: return Qt::green; - case YellowOnBlack: + case Wasp: return QColor::fromHsv(240, 255, 255); - case BlueOnBlack: + case Ice: return Qt::red; case Sunset: @@ -278,6 +306,16 @@ case HighGain: return true; + case Green: + case Sunset: + case WhiteOnBlack: + case Cherry: + case Wasp: + case Ice: + case FruitSalad: + case Banded: + case Highlight: + default: return false; } diff -r 282f4be8f058 -r c02c51ae5238 layer/ColourMapper.h --- a/layer/ColourMapper.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/ColourMapper.h Wed Apr 20 12:06:28 2016 +0100 @@ -29,17 +29,17 @@ Q_OBJECT public: - ColourMapper(int map, float minValue, float maxValue); + ColourMapper(int map, double minValue, double maxValue); virtual ~ColourMapper(); enum StandardMap { - DefaultColours, + Green, Sunset, WhiteOnBlack, BlackOnWhite, - RedOnBlue, - YellowOnBlack, - BlueOnBlack, + Cherry, + Wasp, + Ice, FruitSalad, Banded, Highlight, @@ -48,21 +48,21 @@ }; int getMap() const { return m_map; } - float getMinValue() const { return m_min; } - float getMaxValue() const { return m_max; } + double getMinValue() const { return m_min; } + double getMaxValue() const { return m_max; } static int getColourMapCount(); static QString getColourMapName(int n); - QColor map(float value) const; + QColor map(double value) const; QColor getContrastingColour() const; // for cursors etc bool hasLightBackground() const; protected: int m_map; - float m_min; - float m_max; + double m_min; + double m_max; }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/ColourScaleLayer.h --- a/layer/ColourScaleLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/ColourScaleLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -19,11 +19,13 @@ #include #include +class LayerGeometryProvider; + class ColourScaleLayer { public: virtual QString getScaleUnits() const = 0; - virtual QColor getColourForValue(View *v, float value) const = 0; + virtual QColor getColourForValue(LayerGeometryProvider *v, double value) const = 0; }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/FlexiNoteLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/FlexiNoteLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,1919 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "FlexiNoteLayer.h" + +#include "data/model/Model.h" +#include "data/model/SparseTimeValueModel.h" +#include "base/RealTime.h" +#include "base/Profiler.h" +#include "base/Pitch.h" +#include "base/LogRange.h" +#include "base/RangeMapper.h" +#include "ColourDatabase.h" +#include "view/View.h" + +#include "PianoScale.h" +#include "LinearNumericalScale.h" +#include "LogNumericalScale.h" + +#include "data/model/FlexiNoteModel.h" + +#include "widgets/ItemEditDialog.h" +#include "widgets/TextAbbrev.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include // GF: included to compile std::numerical_limits on linux +#include + + +FlexiNoteLayer::FlexiNoteLayer() : + SingleColourLayer(), + + // m_model(0), + // m_editing(false), + // m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")), + // m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")), + // m_editingCommand(0), + // m_verticalScale(AutoAlignScale), + // m_scaleMinimum(0), + // m_scaleMaximum(0) + + m_model(0), + m_editing(false), + m_intelligentActions(true), + m_dragPointX(0), + m_dragPointY(0), + m_dragStartX(0), + m_dragStartY(0), + m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")), + m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")), + m_greatestLeftNeighbourFrame(0), + m_smallestRightNeighbourFrame(0), + m_editingCommand(0), + m_verticalScale(AutoAlignScale), + m_editMode(DragNote), + m_scaleMinimum(34), + m_scaleMaximum(77) +{ +} + +void +FlexiNoteLayer::setModel(FlexiNoteModel *model) +{ + if (m_model == model) return; + m_model = model; + + connectSignals(m_model); + + // m_scaleMinimum = 0; + // m_scaleMaximum = 0; + + emit modelReplaced(); +} + +Layer::PropertyList +FlexiNoteLayer::getProperties() const +{ + PropertyList list = SingleColourLayer::getProperties(); + list.push_back("Vertical Scale"); + list.push_back("Scale Units"); + return list; +} + +QString +FlexiNoteLayer::getPropertyLabel(const PropertyName &name) const +{ + if (name == "Vertical Scale") return tr("Vertical Scale"); + if (name == "Scale Units") return tr("Scale Units"); + return SingleColourLayer::getPropertyLabel(name); +} + +Layer::PropertyType +FlexiNoteLayer::getPropertyType(const PropertyName &name) const +{ + if (name == "Scale Units") return UnitsProperty; + if (name == "Vertical Scale") return ValueProperty; + return SingleColourLayer::getPropertyType(name); +} + +QString +FlexiNoteLayer::getPropertyGroupName(const PropertyName &name) const +{ + if (name == "Vertical Scale" || name == "Scale Units") { + return tr("Scale"); + } + return SingleColourLayer::getPropertyGroupName(name); +} + +QString +FlexiNoteLayer::getScaleUnits() const +{ + if (m_model) return m_model->getScaleUnits(); + else return ""; +} + +int +FlexiNoteLayer::getPropertyRangeAndValue(const PropertyName &name, + int *min, int *max, int *deflt) const +{ + int val = 0; + + if (name == "Vertical Scale") { + + if (min) *min = 0; + if (max) *max = 3; + if (deflt) *deflt = int(AutoAlignScale); + + val = int(m_verticalScale); + + } else if (name == "Scale Units") { + + if (deflt) *deflt = 0; + if (m_model) { + val = UnitDatabase::getInstance()->getUnitId + (getScaleUnits()); + } + + } else { + + val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); + } + + return val; +} + +QString +FlexiNoteLayer::getPropertyValueLabel(const PropertyName &name, + int value) const +{ + if (name == "Vertical Scale") { + switch (value) { + default: + case 0: return tr("Auto-Align"); + case 1: return tr("Linear"); + case 2: return tr("Log"); + case 3: return tr("MIDI Notes"); + } + } + return SingleColourLayer::getPropertyValueLabel(name, value); +} + +void +FlexiNoteLayer::setProperty(const PropertyName &name, int value) +{ + if (name == "Vertical Scale") { + setVerticalScale(VerticalScale(value)); + } else if (name == "Scale Units") { + if (m_model) { + m_model->setScaleUnits + (UnitDatabase::getInstance()->getUnitById(value)); + emit modelChanged(); + } + } else { + return SingleColourLayer::setProperty(name, value); + } +} + +void +FlexiNoteLayer::setVerticalScale(VerticalScale scale) +{ + if (m_verticalScale == scale) return; + m_verticalScale = scale; + emit layerParametersChanged(); +} + +bool +FlexiNoteLayer::isLayerScrollable(const LayerGeometryProvider *v) const +{ + QPoint discard; + return !v->shouldIlluminateLocalFeatures(this, discard); +} + +bool +FlexiNoteLayer::shouldConvertMIDIToHz() const +{ + QString unit = getScaleUnits(); + return (unit != "Hz"); +// if (unit == "" || +// unit.startsWith("MIDI") || +// unit.startsWith("midi")) return true; +// return false; +} + +bool +FlexiNoteLayer::getValueExtents(double &min, double &max, + bool &logarithmic, QString &unit) const +{ + if (!m_model) return false; + min = m_model->getValueMinimum(); + max = m_model->getValueMaximum(); + + if (shouldConvertMIDIToHz()) { + unit = "Hz"; + min = Pitch::getFrequencyForPitch(int(lrint(min))); + max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); + } else unit = getScaleUnits(); + + if (m_verticalScale == MIDIRangeScale || + m_verticalScale == LogScale) logarithmic = true; + + return true; +} + +bool +FlexiNoteLayer::getDisplayExtents(double &min, double &max) const +{ + if (!m_model || shouldAutoAlign()) { +// std::cerr << "No model or shouldAutoAlign()" << std::endl; + return false; + } + + if (m_verticalScale == MIDIRangeScale) { + min = Pitch::getFrequencyForPitch(0); + max = Pitch::getFrequencyForPitch(127); + return true; + } + + if (m_scaleMinimum == m_scaleMaximum) { + min = m_model->getValueMinimum(); + max = m_model->getValueMaximum(); + } else { + min = m_scaleMinimum; + max = m_scaleMaximum; + } + + if (shouldConvertMIDIToHz()) { + min = Pitch::getFrequencyForPitch(int(lrint(min))); + max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); + } + +#ifdef DEBUG_NOTE_LAYER + cerr << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl; +#endif + + return true; +} + +bool +FlexiNoteLayer::setDisplayExtents(double min, double max) +{ + if (!m_model) return false; + + if (min == max) { + if (min == 0.f) { + max = 1.f; + } else { + max = min * 1.0001f; + } + } + + m_scaleMinimum = min; + m_scaleMaximum = max; + +#ifdef DEBUG_NOTE_LAYER + cerr << "FlexiNoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl; +#endif + + emit layerParametersChanged(); + return true; +} + +int +FlexiNoteLayer::getVerticalZoomSteps(int &defaultStep) const +{ + if (shouldAutoAlign()) return 0; + if (!m_model) return 0; + + defaultStep = 0; + return 100; +} + +int +FlexiNoteLayer::getCurrentVerticalZoomStep() const +{ + if (shouldAutoAlign()) return 0; + if (!m_model) return 0; + + RangeMapper *mapper = getNewVerticalZoomRangeMapper(); + if (!mapper) return 0; + + double dmin, dmax; + getDisplayExtents(dmin, dmax); + + int nr = mapper->getPositionForValue(dmax - dmin); + + delete mapper; + + return 100 - nr; +} + +//!!! lots of duplication with TimeValueLayer + +void +FlexiNoteLayer::setVerticalZoomStep(int step) +{ + if (shouldAutoAlign()) return; + if (!m_model) return; + + RangeMapper *mapper = getNewVerticalZoomRangeMapper(); + if (!mapper) return; + + double min, max; + bool logarithmic; + QString unit; + getValueExtents(min, max, logarithmic, unit); + + double dmin, dmax; + getDisplayExtents(dmin, dmax); + + double newdist = mapper->getValueForPosition(100 - step); + + double newmin, newmax; + + if (logarithmic) { + + // see SpectrogramLayer::setVerticalZoomStep + + newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2; + newmin = newmax - newdist; + +// cerr << "newmin = " << newmin << ", newmax = " << newmax << endl; + + } else { + double dmid = (dmax + dmin) / 2; + newmin = dmid - newdist / 2; + newmax = dmid + newdist / 2; + } + + if (newmin < min) { + newmax += (min - newmin); + newmin = min; + } + if (newmax > max) { + newmax = max; + } + +#ifdef DEBUG_NOTE_LAYER + cerr << "FlexiNoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl; +#endif + + setDisplayExtents(newmin, newmax); +} + +RangeMapper * +FlexiNoteLayer::getNewVerticalZoomRangeMapper() const +{ + if (!m_model) return 0; + + RangeMapper *mapper; + + double min, max; + bool logarithmic; + QString unit; + getValueExtents(min, max, logarithmic, unit); + + if (min == max) return 0; + + if (logarithmic) { + mapper = new LogRangeMapper(0, 100, min, max, unit); + } else { + mapper = new LinearRangeMapper(0, 100, min, max, unit); + } + + return mapper; +} + +FlexiNoteModel::PointList +FlexiNoteLayer::getLocalPoints(LayerGeometryProvider *v, int x) const +{ + if (!m_model) return FlexiNoteModel::PointList(); + + sv_frame_t frame = v->getFrameForX(x); + + FlexiNoteModel::PointList onPoints = + m_model->getPoints(frame); + + if (!onPoints.empty()) { + return onPoints; + } + + FlexiNoteModel::PointList prevPoints = + m_model->getPreviousPoints(frame); + FlexiNoteModel::PointList nextPoints = + m_model->getNextPoints(frame); + + FlexiNoteModel::PointList usePoints = prevPoints; + + if (prevPoints.empty()) { + usePoints = nextPoints; + } else if (prevPoints.begin()->frame < v->getStartFrame() && + !(nextPoints.begin()->frame > v->getEndFrame())) { + usePoints = nextPoints; + } else if (nextPoints.begin()->frame - frame < + frame - prevPoints.begin()->frame) { + usePoints = nextPoints; + } + + if (!usePoints.empty()) { + int fuzz = 2; + int px = v->getXForFrame(usePoints.begin()->frame); + if ((px > x && px - x > fuzz) || + (px < x && x - px > fuzz + 1)) { + usePoints.clear(); + } + } + + return usePoints; +} + +bool +FlexiNoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &p) const +{ + if (!m_model) return false; + + sv_frame_t frame = v->getFrameForX(x); + + FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); + if (onPoints.empty()) return false; + +// cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << endl; + + int nearestDistance = -1; + + for (FlexiNoteModel::PointList::const_iterator i = onPoints.begin(); + i != onPoints.end(); ++i) { + + int distance = getYForValue(v, (*i).value) - y; + if (distance < 0) distance = -distance; + if (nearestDistance == -1 || distance < nearestDistance) { + nearestDistance = distance; + p = *i; + } + } + + return true; +} + +bool +FlexiNoteLayer::getNoteToEdit(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &p) const +{ + // GF: find the note that is closest to the cursor + if (!m_model) return false; + + sv_frame_t frame = v->getFrameForX(x); + + FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); + if (onPoints.empty()) return false; + +// std::cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << std::endl; + + int nearestDistance = -1; + + for (FlexiNoteModel::PointList::const_iterator i = onPoints.begin(); + i != onPoints.end(); ++i) { + + int distance = getYForValue(v, (*i).value) - y; + if (distance < 0) distance = -distance; + if (nearestDistance == -1 || distance < nearestDistance) { + nearestDistance = distance; + p = *i; + } + } + + return true; +} + +QString +FlexiNoteLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const +{ + int x = pos.x(); + + if (!m_model || !m_model->getSampleRate()) return ""; + + FlexiNoteModel::PointList points = getLocalPoints(v, x); + + if (points.empty()) { + if (!m_model->isReady()) { + return tr("In progress"); + } else { + return tr("No local points"); + } + } + + FlexiNote note(0); + FlexiNoteModel::PointList::iterator i; + + for (i = points.begin(); i != points.end(); ++i) { + + int y = getYForValue(v, i->value); + int h = NOTE_HEIGHT; // GF: larger notes + + if (m_model->getValueQuantization() != 0.0) { + h = y - getYForValue(v, i->value + m_model->getValueQuantization()); + if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; + } + + // GF: this is not quite correct + if (pos.y() >= y - 4 && pos.y() <= y + h) { + note = *i; + break; + } + } + + if (i == points.end()) return tr("No local points"); + + RealTime rt = RealTime::frame2RealTime(note.frame, + m_model->getSampleRate()); + RealTime rd = RealTime::frame2RealTime(note.duration, + m_model->getSampleRate()); + + QString pitchText; + + if (shouldConvertMIDIToHz()) { + + int mnote = int(lrint(note.value)); + int cents = int(lrint((note.value - double(mnote)) * 100)); + double freq = Pitch::getFrequencyForPitch(mnote, cents); + pitchText = tr("%1 (%2, %3 Hz)") + .arg(Pitch::getPitchLabel(mnote, cents)) + .arg(mnote) + .arg(freq); + + } else if (getScaleUnits() == "Hz") { + + pitchText = tr("%1 Hz (%2, %3)") + .arg(note.value) + .arg(Pitch::getPitchLabelForFrequency(note.value)) + .arg(Pitch::getPitchForFrequency(note.value)); + + } else { + pitchText = tr("%1 %2") + .arg(note.value).arg(getScaleUnits()); + } + + QString text; + + if (note.label == "") { + text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) + .arg(rt.toText(true).c_str()) + .arg(pitchText) + .arg(rd.toText(true).c_str()); + } else { + text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) + .arg(rt.toText(true).c_str()) + .arg(pitchText) + .arg(rd.toText(true).c_str()) + .arg(note.label); + } + + pos = QPoint(v->getXForFrame(note.frame), + getYForValue(v, note.value)); + return text; +} + +bool +FlexiNoteLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, + SnapType snap) const +{ + if (!m_model) { + return Layer::snapToFeatureFrame(v, frame, resolution, snap); + } + + resolution = m_model->getResolution(); + FlexiNoteModel::PointList points; + + if (snap == SnapNeighbouring) { + + points = getLocalPoints(v, v->getXForFrame(frame)); + if (points.empty()) return false; + frame = points.begin()->frame; + return true; + } + + points = m_model->getPoints(frame, frame); + sv_frame_t snapped = frame; + bool found = false; + + for (FlexiNoteModel::PointList::const_iterator i = points.begin(); + i != points.end(); ++i) { + + cerr << "FlexiNoteModel: point at " << i->frame << endl; + + if (snap == SnapRight) { + + if (i->frame > frame) { + snapped = i->frame; + found = true; + break; + } else if (i->frame + i->duration >= frame) { + snapped = i->frame + i->duration; + found = true; + break; + } + + } else if (snap == SnapLeft) { + + if (i->frame <= frame) { + snapped = i->frame; + found = true; // don't break, as the next may be better + } else { + break; + } + + } else { // nearest + + FlexiNoteModel::PointList::const_iterator j = i; + ++j; + + if (j == points.end()) { + + snapped = i->frame; + found = true; + break; + + } else if (j->frame >= frame) { + + if (j->frame - frame < frame - i->frame) { + snapped = j->frame; + } else { + snapped = i->frame; + } + found = true; + break; + } + } + } + + cerr << "snapToFeatureFrame: frame " << frame << " -> snapped " << snapped << ", found = " << found << endl; + + frame = snapped; + return found; +} + +void +FlexiNoteLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const +{ + min = 0.0; + max = 0.0; + log = false; + + QString queryUnits; + if (shouldConvertMIDIToHz()) queryUnits = "Hz"; + else queryUnits = getScaleUnits(); + + if (shouldAutoAlign()) { + + if (!v->getValueExtents(queryUnits, min, max, log)) { + + min = m_model->getValueMinimum(); + max = m_model->getValueMaximum(); + + if (shouldConvertMIDIToHz()) { + min = Pitch::getFrequencyForPitch(int(lrint(min))); + max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); + } + +#ifdef DEBUG_NOTE_LAYER + cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; +#endif + + } else if (log) { + + LogRange::mapRange(min, max); + +#ifdef DEBUG_NOTE_LAYER + cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; +#endif + } + + } else { + + getDisplayExtents(min, max); + + if (m_verticalScale == MIDIRangeScale) { + min = Pitch::getFrequencyForPitch(0); + max = Pitch::getFrequencyForPitch(70); + } else if (shouldConvertMIDIToHz()) { + min = Pitch::getFrequencyForPitch(int(lrint(min))); + max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); + } + + if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { + LogRange::mapRange(min, max); + log = true; + } + } + + if (max == min) max = min + 1.0; +} + +int +FlexiNoteLayer::getYForValue(LayerGeometryProvider *v, double val) const +{ + double min = 0.0, max = 0.0; + bool logarithmic = false; + int h = v->getPaintHeight(); + + getScaleExtents(v, min, max, logarithmic); + +#ifdef DEBUG_NOTE_LAYER + cerr << "FlexiNoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl; +#endif + + if (shouldConvertMIDIToHz()) { + val = Pitch::getFrequencyForPitch(int(lrint(val)), + int(lrint((val - floor(val)) * 100.0))); +#ifdef DEBUG_NOTE_LAYER + cerr << "shouldConvertMIDIToHz true, val now = " << val << endl; +#endif + } + + if (logarithmic) { + val = LogRange::map(val); +#ifdef DEBUG_NOTE_LAYER + cerr << "logarithmic true, val now = " << val << endl; +#endif + } + + int y = int(h - ((val - min) * h) / (max - min)) - 1; +#ifdef DEBUG_NOTE_LAYER + cerr << "y = " << y << endl; +#endif + return y; +} + +double +FlexiNoteLayer::getValueForY(LayerGeometryProvider *v, int y) const +{ + double min = 0.0, max = 0.0; + bool logarithmic = false; + int h = v->getPaintHeight(); + + getScaleExtents(v, min, max, logarithmic); + + double val = min + (double(h - y) * double(max - min)) / h; + + if (logarithmic) { + val = pow(10.f, val); + } + + if (shouldConvertMIDIToHz()) { + val = Pitch::getPitchForFrequency(val); + } + + return val; +} + +bool +FlexiNoteLayer::shouldAutoAlign() const +{ + if (!m_model) return false; + return (m_verticalScale == AutoAlignScale); +} + +void +FlexiNoteLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const +{ + if (!m_model || !m_model->isOK()) return; + + sv_samplerate_t sampleRate = m_model->getSampleRate(); + if (!sampleRate) return; + +// Profiler profiler("FlexiNoteLayer::paint", true); + + int x1 = rect.right(); + sv_frame_t frame1 = v->getFrameForX(x1); + + FlexiNoteModel::PointList points(m_model->getPoints(0, frame1)); + if (points.empty()) return; + + paint.setPen(getBaseQColor()); + + QColor brushColour(getBaseQColor()); + brushColour.setAlpha(80); + +// SVDEBUG << "FlexiNoteLayer::paint: resolution is " +// << m_model->getResolution() << " frames" << endl; + + double min = m_model->getValueMinimum(); + double max = m_model->getValueMaximum(); + if (max == min) max = min + 1.0; + + QPoint localPos; + FlexiNoteModel::Point illuminatePoint(0); + bool shouldIlluminate = false; + + if (v->shouldIlluminateLocalFeatures(this, localPos)) { + shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), + illuminatePoint); + } + + paint.save(); + paint.setRenderHint(QPainter::Antialiasing, false); + + int noteNumber = 0; + + for (FlexiNoteModel::PointList::const_iterator i = points.begin(); + i != points.end(); ++i) { + + ++noteNumber; + const FlexiNoteModel::Point &p(*i); + + int x = v->getXForFrame(p.frame); + int y = getYForValue(v, p.value); + int w = v->getXForFrame(p.frame + p.duration) - x; + int h = NOTE_HEIGHT; //GF: larger notes + + if (m_model->getValueQuantization() != 0.0) { + h = y - getYForValue(v, p.value + m_model->getValueQuantization()); + if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; //GF: larger notes + } + + if (w < 1) w = 1; + paint.setPen(getBaseQColor()); + paint.setBrush(brushColour); + + if (shouldIlluminate && + // "illuminatePoint == p" + !FlexiNoteModel::Point::Comparator()(illuminatePoint, p) && + !FlexiNoteModel::Point::Comparator()(p, illuminatePoint)) { + + paint.drawLine(x, -1, x, v->getPaintHeight() + 1); + paint.drawLine(x+w, -1, x+w, v->getPaintHeight() + 1); + + paint.setPen(v->getForeground()); + // paint.setBrush(v->getForeground()); + + QString vlabel = QString("freq: %1%2").arg(p.value).arg(m_model->getScaleUnits()); + // v->drawVisibleText(paint, + // x - paint.fontMetrics().width(vlabel) - 2, + // y + paint.fontMetrics().height()/2 + // - paint.fontMetrics().descent(), + // vlabel, View::OutlinedText); + v->drawVisibleText(paint, + x, + y - h/2 - 2 - paint.fontMetrics().height() + - paint.fontMetrics().descent(), + vlabel, View::OutlinedText); + + QString hlabel = "dur: " + QString(RealTime::frame2RealTime + (p.duration, m_model->getSampleRate()).toText(true).c_str()); + v->drawVisibleText(paint, + x, + y - h/2 - paint.fontMetrics().descent() - 2, + hlabel, View::OutlinedText); + + QString llabel = QString("%1").arg(p.label); + v->drawVisibleText(paint, + x, + y + h + 2 + paint.fontMetrics().descent(), + llabel, View::OutlinedText); + QString nlabel = QString("%1").arg(noteNumber); + v->drawVisibleText(paint, + x + paint.fontMetrics().averageCharWidth() / 2, + y + h/2 - paint.fontMetrics().descent(), + nlabel, View::OutlinedText); + } + + paint.drawRect(x, y - h/2, w, h); + } + + paint.restore(); +} + +int +FlexiNoteLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const +{ + if (!m_model || shouldAutoAlign()) { + return 0; + } else { + if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { + return LogNumericalScale().getWidth(v, paint) + 10; // for piano + } else { + return LinearNumericalScale().getWidth(v, paint); + } + } +} + +void +FlexiNoteLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const +{ + if (!m_model || m_model->getPoints().empty()) return; + + QString unit; + double min, max; + bool logarithmic; + + int w = getVerticalScaleWidth(v, false, paint); + int h = v->getPaintHeight(); + + getScaleExtents(v, min, max, logarithmic); + + if (logarithmic) { + LogNumericalScale().paintVertical(v, this, paint, 0, min, max); + } else { + LinearNumericalScale().paintVertical(v, this, paint, 0, min, max); + } + + if (logarithmic && (getScaleUnits() == "Hz")) { + PianoScale().paintPianoVertical + (v, paint, QRect(w - 10, 0, 10, h), + LogRange::unmap(min), + LogRange::unmap(max)); + paint.drawLine(w, 0, w, h); + } + + if (getScaleUnits() != "") { + int mw = w - 5; + paint.drawText(5, + 5 + paint.fontMetrics().ascent(), + TextAbbrev::abbreviate(getScaleUnits(), + paint.fontMetrics(), + mw)); + } +} + +void +FlexiNoteLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) +{ +// SVDEBUG << "FlexiNoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; + + if (!m_model) return; + + sv_frame_t frame = v->getFrameForX(e->x()); + if (frame < 0) frame = 0; + frame = frame / m_model->getResolution() * m_model->getResolution(); + + double value = getValueForY(v, e->y()); + + m_editingPoint = FlexiNoteModel::Point(frame, float(value), 0, 0.8f, tr("New Point")); + m_originalPoint = m_editingPoint; + + if (m_editingCommand) finish(m_editingCommand); + m_editingCommand = new FlexiNoteModel::EditCommand(m_model, + tr("Draw Point")); + m_editingCommand->addPoint(m_editingPoint); + + m_editing = true; +} + +void +FlexiNoteLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) +{ +// SVDEBUG << "FlexiNoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; + + if (!m_model || !m_editing) return; + + sv_frame_t frame = v->getFrameForX(e->x()); + if (frame < 0) frame = 0; + frame = frame / m_model->getResolution() * m_model->getResolution(); + + double newValue = getValueForY(v, e->y()); + + sv_frame_t newFrame = m_editingPoint.frame; + sv_frame_t newDuration = frame - newFrame; + if (newDuration < 0) { + newFrame = frame; + newDuration = -newDuration; + } else if (newDuration == 0) { + newDuration = 1; + } + + m_editingCommand->deletePoint(m_editingPoint); + m_editingPoint.frame = newFrame; + m_editingPoint.value = float(newValue); + m_editingPoint.duration = newDuration; + m_editingCommand->addPoint(m_editingPoint); +} + +void +FlexiNoteLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *) +{ +// SVDEBUG << "FlexiNoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl; + if (!m_model || !m_editing) return; + finish(m_editingCommand); + m_editingCommand = 0; + m_editing = false; +} + +void +FlexiNoteLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) +{ + if (!m_model) return; + + if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; + + if (m_editingCommand) { + finish(m_editingCommand); + m_editingCommand = 0; + } + + m_editing = true; +} + +void +FlexiNoteLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *) +{ +} + +void +FlexiNoteLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e) +{ + if (!m_model || !m_editing) return; + + m_editing = false; + + FlexiNoteModel::Point p(0); + if (!getPointToDrag(v, e->x(), e->y(), p)) return; + if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return; + + m_editingCommand = new FlexiNoteModel::EditCommand(m_model, tr("Erase Point")); + + m_editingCommand->deletePoint(m_editingPoint); + + finish(m_editingCommand); + m_editingCommand = 0; + m_editing = false; +} + +void +FlexiNoteLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) +{ +// SVDEBUG << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; + std::cerr << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; + + if (!m_model) return; + + if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; + m_originalPoint = FlexiNote(m_editingPoint); + + if (m_editMode == RightBoundary) { + m_dragPointX = v->getXForFrame(m_editingPoint.frame + m_editingPoint.duration); + } else { + m_dragPointX = v->getXForFrame(m_editingPoint.frame); + } + m_dragPointY = getYForValue(v, m_editingPoint.value); + + if (m_editingCommand) { + finish(m_editingCommand); + m_editingCommand = 0; + } + + m_editing = true; + m_dragStartX = e->x(); + m_dragStartY = e->y(); + + sv_frame_t onset = m_originalPoint.frame; + sv_frame_t offset = m_originalPoint.frame + m_originalPoint.duration - 1; + + m_greatestLeftNeighbourFrame = -1; + m_smallestRightNeighbourFrame = std::numeric_limits::max(); + + for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin(); + i != m_model->getPoints().end(); ++i) { + FlexiNote currentNote = *i; + + // left boundary + if (currentNote.frame + currentNote.duration - 1 < onset) { + m_greatestLeftNeighbourFrame = currentNote.frame + currentNote.duration - 1; + } + + // right boundary + if (currentNote.frame > offset) { + m_smallestRightNeighbourFrame = currentNote.frame; + break; + } + } + std::cerr << "editStart: mode is " << m_editMode << ", note frame: " << onset << ", left boundary: " << m_greatestLeftNeighbourFrame << ", right boundary: " << m_smallestRightNeighbourFrame << std::endl; +} + +void +FlexiNoteLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) +{ +// SVDEBUG << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl; + std::cerr << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl; + + if (!m_model || !m_editing) return; + + int xdist = e->x() - m_dragStartX; + int ydist = e->y() - m_dragStartY; + int newx = m_dragPointX + xdist; + int newy = m_dragPointY + ydist; + + sv_frame_t dragFrame = v->getFrameForX(newx); + if (dragFrame < 0) dragFrame = 0; + dragFrame = dragFrame / m_model->getResolution() * m_model->getResolution(); + + double value = getValueForY(v, newy); + + if (!m_editingCommand) { + m_editingCommand = new FlexiNoteModel::EditCommand(m_model, + tr("Drag Point")); + } + + m_editingCommand->deletePoint(m_editingPoint); + + std::cerr << "edit mode: " << m_editMode << " intelligent actions = " + << m_intelligentActions << std::endl; + + switch (m_editMode) { + case LeftBoundary : { + // left + if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1; + // right + if (m_intelligentActions && dragFrame >= m_originalPoint.frame + m_originalPoint.duration) { + dragFrame = m_originalPoint.frame + m_originalPoint.duration - 1; + } + m_editingPoint.frame = dragFrame; + m_editingPoint.duration = m_originalPoint.frame - dragFrame + m_originalPoint.duration; + break; + } + case RightBoundary : { + // left + if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1; + if (m_intelligentActions && dragFrame >= m_smallestRightNeighbourFrame) dragFrame = m_smallestRightNeighbourFrame - 1; + m_editingPoint.duration = dragFrame - m_originalPoint.frame + 1; + break; + } + case DragNote : { + // left + if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1; + // right + if (m_intelligentActions && dragFrame + m_originalPoint.duration >= m_smallestRightNeighbourFrame) { + dragFrame = m_smallestRightNeighbourFrame - m_originalPoint.duration; + } + m_editingPoint.frame = dragFrame; + + m_editingPoint.value = float(value); + + // Re-analyse region within +/- 1 semitone of the dragged value + float cents = 0; + int midiPitch = Pitch::getPitchForFrequency(m_editingPoint.value, ¢s); + double lower = Pitch::getFrequencyForPitch(midiPitch - 1, cents); + double higher = Pitch::getFrequencyForPitch(midiPitch + 1, cents); + + emit reAnalyseRegion(m_editingPoint.frame, + m_editingPoint.frame + m_editingPoint.duration, + float(lower), float(higher)); + break; + } + case SplitNote: // nothing + break; + } + +// updateNoteValueFromPitchCurve(v, m_editingPoint); + m_editingCommand->addPoint(m_editingPoint); + + std::cerr << "added new point(" << m_editingPoint.frame << "," << m_editingPoint.duration << ")" << std::endl; +} + +void +FlexiNoteLayer::editEnd(LayerGeometryProvider *v, QMouseEvent *e) +{ +// SVDEBUG << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl; + std::cerr << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; + + if (!m_model || !m_editing) return; + + if (m_editingCommand) { + + QString newName = m_editingCommand->getName(); + + if (m_editMode == DragNote) { + //!!! command nesting is wrong? + emit materialiseReAnalysis(); + } + + m_editingCommand->deletePoint(m_editingPoint); + updateNoteValueFromPitchCurve(v, m_editingPoint); + m_editingCommand->addPoint(m_editingPoint); + + if (m_editingPoint.frame != m_originalPoint.frame) { + if (m_editingPoint.value != m_originalPoint.value) { + newName = tr("Edit Point"); + } else { + newName = tr("Relocate Point"); + } + } else { + newName = tr("Change Point Value"); + } + + m_editingCommand->setName(newName); + finish(m_editingCommand); + } + + m_editingCommand = 0; + m_editing = false; +} + +void +FlexiNoteLayer::splitStart(LayerGeometryProvider *v, QMouseEvent *e) +{ + // GF: note splitting starts (!! remove printing soon) + std::cerr << "splitStart (n.b. editStart will be called later, if the user drags the mouse)" << std::endl; + if (!m_model) return; + + if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; + // m_originalPoint = m_editingPoint; + // + // m_dragPointX = v->getXForFrame(m_editingPoint.frame); + // m_dragPointY = getYForValue(v, m_editingPoint.value); + + if (m_editingCommand) { + finish(m_editingCommand); + m_editingCommand = 0; + } + + m_editing = true; + m_dragStartX = e->x(); + m_dragStartY = e->y(); +} + +void +FlexiNoteLayer::splitEnd(LayerGeometryProvider *v, QMouseEvent *e) +{ + // GF: note splitting ends. (!! remove printing soon) + std::cerr << "splitEnd" << std::endl; + if (!m_model || !m_editing || m_editMode != SplitNote) return; + + int xdist = e->x() - m_dragStartX; + int ydist = e->y() - m_dragStartY; + if (xdist != 0 || ydist != 0) { + std::cerr << "mouse moved" << std::endl; + return; + } + + sv_frame_t frame = v->getFrameForX(e->x()); + + splitNotesAt(v, frame, e); +} + +void +FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame) +{ + splitNotesAt(v, frame, 0); +} + +void +FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame, QMouseEvent *e) +{ + FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); + if (onPoints.empty()) return; + + FlexiNote note(*onPoints.begin()); + + FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + (m_model, tr("Edit Point")); + command->deletePoint(note); + + if (!e || !(e->modifiers() & Qt::ShiftModifier)) { + + int gap = 0; // MM: I prefer a gap of 0, but we can decide later + + FlexiNote newNote1(note.frame, note.value, + frame - note.frame - gap, + note.level, note.label); + + FlexiNote newNote2(frame, note.value, + note.duration - newNote1.duration, + note.level, note.label); + + if (m_intelligentActions) { + if (updateNoteValueFromPitchCurve(v, newNote1)) { + command->addPoint(newNote1); + } + if (updateNoteValueFromPitchCurve(v, newNote2)) { + command->addPoint(newNote2); + } + } else { + command->addPoint(newNote1); + command->addPoint(newNote2); + } + } + + finish(command); +} + +void +FlexiNoteLayer::addNote(LayerGeometryProvider *v, QMouseEvent *e) +{ + std::cerr << "addNote" << std::endl; + if (!m_model) return; + + sv_frame_t duration = 10000; + + sv_frame_t frame = v->getFrameForX(e->x()); + double value = getValueForY(v, e->y()); + + FlexiNoteModel::PointList noteList = m_model->getPoints(); + + if (m_intelligentActions) { + sv_frame_t smallestRightNeighbourFrame = 0; + for (FlexiNoteModel::PointList::const_iterator i = noteList.begin(); + i != noteList.end(); ++i) { + FlexiNote currentNote = *i; + if (currentNote.frame > frame) { + smallestRightNeighbourFrame = currentNote.frame; + break; + } + } + if (smallestRightNeighbourFrame > 0) { + duration = std::min(smallestRightNeighbourFrame - frame + 1, duration); + duration = (duration > 0) ? duration : 0; + } + } + + if (!m_intelligentActions || + (m_model->getPoints(frame).empty() && duration > 0)) { + FlexiNote newNote(frame, float(value), duration, 100.f, "new note"); + FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + (m_model, tr("Add Point")); + command->addPoint(newNote); + finish(command); + } +} + +SparseTimeValueModel * +FlexiNoteLayer::getAssociatedPitchModel(LayerGeometryProvider *v) const +{ + // Better than we used to do, but still not very satisfactory + +// cerr << "FlexiNoteLayer::getAssociatedPitchModel()" << endl; + + for (int i = 0; i < v->getView()->getLayerCount(); ++i) { + Layer *layer = v->getView()->getLayer(i); + if (layer && + layer->getLayerPresentationName() != "candidate") { +// cerr << "FlexiNoteLayer::getAssociatedPitchModel: looks like our layer is " << layer << endl; + SparseTimeValueModel *model = qobject_cast + (layer->getModel()); +// cerr << "FlexiNoteLayer::getAssociatedPitchModel: and its model is " << model << endl; + if (model && model->getScaleUnits() == "Hz") { + cerr << "FlexiNoteLayer::getAssociatedPitchModel: it's good, returning " << model << endl; + return model; + } + } + } + cerr << "FlexiNoteLayer::getAssociatedPitchModel: failed to find a model" << endl; + return 0; +} + +void +FlexiNoteLayer::snapSelectedNotesToPitchTrack(LayerGeometryProvider *v, Selection s) +{ + if (!m_model) return; + + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + (m_model, tr("Snap Notes")); + + cerr << "snapSelectedNotesToPitchTrack: selection is from " << s.getStartFrame() << " to " << s.getEndFrame() << endl; + + for (FlexiNoteModel::PointList::iterator i = points.begin(); + i != points.end(); ++i) { + + FlexiNote note(*i); + + cerr << "snapSelectedNotesToPitchTrack: looking at note from " << note.frame << " to " << note.frame + note.duration << endl; + + if (!s.contains(note.frame) && + !s.contains(note.frame + note.duration - 1)) { + continue; + } + + cerr << "snapSelectedNotesToPitchTrack: making new note" << endl; + FlexiNote newNote(note); + + command->deletePoint(note); + + if (updateNoteValueFromPitchCurve(v, newNote)) { + command->addPoint(newNote); + } + } + + finish(command); +} + +void +FlexiNoteLayer::mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive) +{ + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + FlexiNoteModel::PointList::iterator i = points.begin(); + if (inclusive) { + while (i != points.end() && i->frame + i->duration < s.getStartFrame()) { + ++i; + } + } else { + while (i != points.end() && i->frame < s.getStartFrame()) { + ++i; + } + } + + if (i == points.end()) return; + + FlexiNoteModel::EditCommand *command = + new FlexiNoteModel::EditCommand(m_model, tr("Merge Notes")); + + FlexiNote newNote(*i); + + while (i != points.end()) { + + if (inclusive) { + if (i->frame >= s.getEndFrame()) break; + } else { + if (i->frame + i->duration > s.getEndFrame()) break; + } + + newNote.duration = i->frame + i->duration - newNote.frame; + command->deletePoint(*i); + + ++i; + } + + updateNoteValueFromPitchCurve(v, newNote); + command->addPoint(newNote); + finish(command); +} + +bool +FlexiNoteLayer::updateNoteValueFromPitchCurve(LayerGeometryProvider *v, FlexiNoteModel::Point ¬e) const +{ + SparseTimeValueModel *model = getAssociatedPitchModel(v); + if (!model) return false; + + std::cerr << model->getTypeName() << std::endl; + + SparseModel::PointList dataPoints = + model->getPoints(note.frame, note.frame + note.duration); + + std::cerr << "frame " << note.frame << ": " << dataPoints.size() << " candidate points" << std::endl; + + if (dataPoints.empty()) return false; + + std::vector pitchValues; + + for (SparseModel::PointList::const_iterator i = + dataPoints.begin(); i != dataPoints.end(); ++i) { + if (i->frame >= note.frame && + i->frame < note.frame + note.duration) { + pitchValues.push_back(i->value); + } + } + + if (pitchValues.empty()) return false; + + sort(pitchValues.begin(), pitchValues.end()); + int size = int(pitchValues.size()); + double median; + + if (size % 2 == 0) { + median = (pitchValues[size/2 - 1] + pitchValues[size/2]) / 2; + } else { + median = pitchValues[size/2]; + } + + std::cerr << "updateNoteValueFromPitchCurve: corrected from " << note.value << " to median " << median << std::endl; + + note.value = float(median); + + return true; +} + +void +FlexiNoteLayer::mouseMoveEvent(LayerGeometryProvider *v, QMouseEvent *e) +{ + // GF: context sensitive cursors + // v->getView()->setCursor(Qt::ArrowCursor); + FlexiNoteModel::Point note(0); + if (!getNoteToEdit(v, e->x(), e->y(), note)) { + // v->getView()->setCursor(Qt::UpArrowCursor); + return; + } + + bool closeToLeft = false, closeToRight = false, + closeToTop = false, closeToBottom = false; + getRelativeMousePosition(v, note, e->x(), e->y(), + closeToLeft, closeToRight, + closeToTop, closeToBottom); + + if (closeToLeft) { + v->getView()->setCursor(Qt::SizeHorCursor); + m_editMode = LeftBoundary; + cerr << "edit mode -> LeftBoundary" << endl; + } else if (closeToRight) { + v->getView()->setCursor(Qt::SizeHorCursor); + m_editMode = RightBoundary; + cerr << "edit mode -> RightBoundary" << endl; + } else if (closeToTop) { + v->getView()->setCursor(Qt::CrossCursor); + m_editMode = DragNote; + cerr << "edit mode -> DragNote" << endl; + } else if (closeToBottom) { + v->getView()->setCursor(Qt::UpArrowCursor); + m_editMode = SplitNote; + cerr << "edit mode -> SplitNote" << endl; + } else { + v->getView()->setCursor(Qt::ArrowCursor); + } +} + +void +FlexiNoteLayer::getRelativeMousePosition(LayerGeometryProvider *v, FlexiNoteModel::Point ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const +{ + // GF: TODO: consoloidate the tolerance values + if (!m_model) return; + + int ctol = 0; + int noteStartX = v->getXForFrame(note.frame); + int noteEndX = v->getXForFrame(note.frame + note.duration); + int noteValueY = getYForValue(v,note.value); + int noteStartY = noteValueY - (NOTE_HEIGHT / 2); + int noteEndY = noteValueY + (NOTE_HEIGHT / 2); + + bool closeToNote = false; + + if (y >= noteStartY-ctol && y <= noteEndY+ctol && x >= noteStartX-ctol && x <= noteEndX+ctol) closeToNote = true; + if (!closeToNote) return; + + int tol = NOTE_HEIGHT / 2; + + if (x >= noteStartX - tol && x <= noteStartX + tol) closeToLeft = true; + if (x >= noteEndX - tol && x <= noteEndX + tol) closeToRight = true; + if (y >= noteStartY - tol && y <= noteStartY + tol) closeToTop = true; + if (y >= noteEndY - tol && y <= noteEndY + tol) closeToBottom = true; + +// cerr << "FlexiNoteLayer::getRelativeMousePosition: close to: left " << closeToLeft << " right " << closeToRight << " top " << closeToTop << " bottom " << closeToBottom << endl; +} + + +bool +FlexiNoteLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) +{ + std::cerr << "Opening note editor dialog" << std::endl; + if (!m_model) return false; + + FlexiNoteModel::Point note(0); + if (!getPointToDrag(v, e->x(), e->y(), note)) return false; + +// FlexiNoteModel::Point note = *points.begin(); + + ItemEditDialog *dialog = new ItemEditDialog + (m_model->getSampleRate(), + ItemEditDialog::ShowTime | + ItemEditDialog::ShowDuration | + ItemEditDialog::ShowValue | + ItemEditDialog::ShowText, + getScaleUnits()); + + dialog->setFrameTime(note.frame); + dialog->setValue(note.value); + dialog->setFrameDuration(note.duration); + dialog->setText(note.label); + + if (dialog->exec() == QDialog::Accepted) { + + FlexiNoteModel::Point newNote = note; + newNote.frame = dialog->getFrameTime(); + newNote.value = dialog->getValue(); + newNote.duration = dialog->getFrameDuration(); + newNote.label = dialog->getText(); + + FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand + (m_model, tr("Edit Point")); + command->deletePoint(note); + command->addPoint(newNote); + finish(command); + } + + delete dialog; + return true; +} + +void +FlexiNoteLayer::moveSelection(Selection s, sv_frame_t newStartFrame) +{ + if (!m_model) return; + + FlexiNoteModel::EditCommand *command = + new FlexiNoteModel::EditCommand(m_model, tr("Drag Selection")); + + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + for (FlexiNoteModel::PointList::iterator i = points.begin(); + i != points.end(); ++i) { + + if (s.contains(i->frame)) { + FlexiNoteModel::Point newPoint(*i); + newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); + command->deletePoint(*i); + command->addPoint(newPoint); + } + } + + finish(command); +} + +void +FlexiNoteLayer::resizeSelection(Selection s, Selection newSize) +{ + if (!m_model) return; + + FlexiNoteModel::EditCommand *command = + new FlexiNoteModel::EditCommand(m_model, tr("Resize Selection")); + + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + double ratio = + double(newSize.getEndFrame() - newSize.getStartFrame()) / + double(s.getEndFrame() - s.getStartFrame()); + + for (FlexiNoteModel::PointList::iterator i = points.begin(); + i != points.end(); ++i) { + + if (s.contains(i->frame)) { + + double targetStart = double(i->frame); + targetStart = double(newSize.getStartFrame()) + + targetStart - double(s.getStartFrame()) * ratio; + + double targetEnd = double(i->frame + i->duration); + targetEnd = double(newSize.getStartFrame()) + + targetEnd - double(s.getStartFrame()) * ratio; + + FlexiNoteModel::Point newPoint(*i); + newPoint.frame = lrint(targetStart); + newPoint.duration = lrint(targetEnd - targetStart); + command->deletePoint(*i); + command->addPoint(newPoint); + } + } + + finish(command); +} + +void +FlexiNoteLayer::deleteSelection(Selection s) +{ + if (!m_model) return; + + FlexiNoteModel::EditCommand *command = + new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points")); + + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + for (FlexiNoteModel::PointList::iterator i = points.begin(); + i != points.end(); ++i) { + + if (s.contains(i->frame)) { + command->deletePoint(*i); + } + } + + finish(command); +} + +void +FlexiNoteLayer::deleteSelectionInclusive(Selection s) +{ + if (!m_model) return; + + FlexiNoteModel::EditCommand *command = + new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points")); + + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + for (FlexiNoteModel::PointList::iterator i = points.begin(); + i != points.end(); ++i) { + bool overlap = !( + ((s.getStartFrame() <= i->frame) && (s.getEndFrame() <= i->frame)) || // selection is left of note + ((s.getStartFrame() >= (i->frame+i->duration)) && (s.getEndFrame() >= (i->frame+i->duration))) // selection is right of note + ); + if (overlap) { + command->deletePoint(*i); + } + } + + finish(command); +} + +void +FlexiNoteLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) +{ + if (!m_model) return; + + FlexiNoteModel::PointList points = + m_model->getPoints(s.getStartFrame(), s.getEndFrame()); + + for (FlexiNoteModel::PointList::iterator i = points.begin(); + i != points.end(); ++i) { + if (s.contains(i->frame)) { + Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label); + point.setReferenceFrame(alignToReference(v, i->frame)); + to.addPoint(point); + } + } +} + +bool +FlexiNoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /*frameOffset */, bool /* interactive */) +{ + if (!m_model) return false; + + const Clipboard::PointList &points = from.getPoints(); + + bool realign = false; + + if (clipboardHasDifferentAlignment(v, from)) { + + QMessageBox::StandardButton button = + QMessageBox::question(v->getView(), tr("Re-align pasted items?"), + tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Yes); + + if (button == QMessageBox::Cancel) { + return false; + } + + if (button == QMessageBox::Yes) { + realign = true; + } + } + + FlexiNoteModel::EditCommand *command = + new FlexiNoteModel::EditCommand(m_model, tr("Paste")); + + for (Clipboard::PointList::const_iterator i = points.begin(); + i != points.end(); ++i) { + + if (!i->haveFrame()) continue; + sv_frame_t frame = 0; + + if (!realign) { + + frame = i->getFrame(); + + } else { + + if (i->haveReferenceFrame()) { + frame = i->getReferenceFrame(); + frame = alignFromReference(v, frame); + } else { + frame = i->getFrame(); + } + } + + FlexiNoteModel::Point newPoint(frame); + + if (i->haveLabel()) newPoint.label = i->getLabel(); + if (i->haveValue()) newPoint.value = i->getValue(); + else newPoint.value = (m_model->getValueMinimum() + + m_model->getValueMaximum()) / 2; + if (i->haveLevel()) newPoint.level = i->getLevel(); + if (i->haveDuration()) newPoint.duration = i->getDuration(); + else { + sv_frame_t nextFrame = frame; + Clipboard::PointList::const_iterator j = i; + for (; j != points.end(); ++j) { + if (!j->haveFrame()) continue; + if (j != i) break; + } + if (j != points.end()) { + nextFrame = j->getFrame(); + } + if (nextFrame == frame) { + newPoint.duration = m_model->getResolution(); + } else { + newPoint.duration = nextFrame - frame; + } + } + + command->addPoint(newPoint); + } + + finish(command); + return true; +} + +void +FlexiNoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity) +{ + m_pendingNoteOns.insert(FlexiNote(frame, float(pitch), 0, float(velocity / 127.0), "")); +} + +void +FlexiNoteLayer::addNoteOff(sv_frame_t frame, int pitch) +{ + for (FlexiNoteSet::iterator i = m_pendingNoteOns.begin(); + i != m_pendingNoteOns.end(); ++i) { + if (lrint((*i).value) == pitch) { + FlexiNote note(*i); + m_pendingNoteOns.erase(i); + note.duration = frame - note.frame; + if (m_model) { + FlexiNoteModel::AddPointCommand *c = new FlexiNoteModel::AddPointCommand + (m_model, note, tr("Record FlexiNote")); + // execute and bundle: + CommandHistory::getInstance()->addCommand(c, true, true); + } + break; + } + } +} + +void +FlexiNoteLayer::abandonNoteOns() +{ + m_pendingNoteOns.clear(); +} + +int +FlexiNoteLayer::getDefaultColourHint(bool darkbg, bool &impose) +{ + impose = false; + return ColourDatabase::getInstance()->getColourIndex + (QString(darkbg ? "White" : "Black")); +} + +void +FlexiNoteLayer::toXml(QTextStream &stream, + QString indent, QString extraAttributes) const +{ + SingleColourLayer::toXml(stream, indent, extraAttributes + + QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ") + .arg(m_verticalScale) + .arg(m_scaleMinimum) + .arg(m_scaleMaximum)); +} + +void +FlexiNoteLayer::setProperties(const QXmlAttributes &attributes) +{ + SingleColourLayer::setProperties(attributes); + + bool ok; + VerticalScale scale = (VerticalScale) + attributes.value("verticalScale").toInt(&ok); + if (ok) setVerticalScale(scale); + +// bool alsoOk; +// double min = attributes.value("scaleMinimum").toDouble(&ok); +// double max = attributes.value("scaleMaximum").toDouble(&alsoOk); +// if (ok && alsoOk && min != max) setDisplayExtents(min, max); +} + +void +FlexiNoteLayer::setVerticalRangeToNoteRange(LayerGeometryProvider *v) +{ + double minf = std::numeric_limits::max(); + double maxf = 0; + bool hasNotes = 0; + for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin(); + i != m_model->getPoints().end(); ++i) { + hasNotes = 1; + FlexiNote note = *i; + if (note.value < minf) minf = note.value; + if (note.value > maxf) maxf = note.value; + } + + std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl; + + if (hasNotes) { + v->getView()->getLayer(1)->setDisplayExtents(minf*0.66,maxf*1.5); + // MM: this is a hack because we rely on + // * this layer being automatically aligned to layer 1 + // * layer one is a log frequency layer. + } +} + + diff -r 282f4be8f058 -r c02c51ae5238 layer/FlexiNoteLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/FlexiNoteLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,215 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _FLEXINOTE_LAYER_H_ +#define _FLEXINOTE_LAYER_H_ + +#define NOTE_HEIGHT 16 + +#include "SingleColourLayer.h" +#include "VerticalScaleLayer.h" + +#include "data/model/FlexiNoteModel.h" + +#include +#include + +class View; +class QPainter; +class SparseTimeValueModel; + +class FlexiNoteLayer : public SingleColourLayer, + public VerticalScaleLayer +{ + Q_OBJECT + +public: + FlexiNoteLayer(); + + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; + + virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const; + virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const; + + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; + + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, + SnapType snap) const; + + virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *); + + virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *); + + virtual void editStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *); + + virtual void splitStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void splitEnd(LayerGeometryProvider *v, QMouseEvent *); + + virtual void addNote(LayerGeometryProvider *v, QMouseEvent *e); + + virtual void mouseMoveEvent(LayerGeometryProvider *v, QMouseEvent *); + + virtual bool editOpen(LayerGeometryProvider *v, QMouseEvent *); + + virtual void moveSelection(Selection s, sv_frame_t newStartFrame); + virtual void resizeSelection(Selection s, Selection newSize); + virtual void deleteSelection(Selection s); + virtual void deleteSelectionInclusive(Selection s); + + virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to); + virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, + bool interactive); + + void splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame); + void snapSelectedNotesToPitchTrack(LayerGeometryProvider *v, Selection s); + void mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive); + + virtual const Model *getModel() const { return m_model; } + void setModel(FlexiNoteModel *model); + + virtual PropertyList getProperties() const; + virtual QString getPropertyLabel(const PropertyName &) const; + virtual PropertyType getPropertyType(const PropertyName &) const; + virtual QString getPropertyGroupName(const PropertyName &) const; + virtual int getPropertyRangeAndValue(const PropertyName &, + int *min, int *max, int *deflt) const; + virtual QString getPropertyValueLabel(const PropertyName &, + int value) const; + virtual void setProperty(const PropertyName &, int value); + + enum VerticalScale { + AutoAlignScale, + LinearScale, + LogScale, + MIDIRangeScale + }; + + //GF: Tonioni: context sensitive note edit actions (denoted clockwise from top). + enum EditMode { + DragNote, + RightBoundary, + SplitNote, + LeftBoundary + }; + + void setIntelligentActions(bool on) { m_intelligentActions=on; } + + void setVerticalScale(VerticalScale scale); + VerticalScale getVerticalScale() const { return m_verticalScale; } + + virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; + + virtual bool isLayerEditable() const { return true; } + + virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); } + + virtual bool getValueExtents(double &min, double &max, + bool &log, QString &unit) const; + + virtual bool getDisplayExtents(double &min, double &max) const; + virtual bool setDisplayExtents(double min, double max); + + virtual int getVerticalZoomSteps(int &defaultStep) const; + virtual int getCurrentVerticalZoomStep() const; + virtual void setVerticalZoomStep(int); + virtual RangeMapper *getNewVerticalZoomRangeMapper() const; + + /** + * Add a note-on. Used when recording MIDI "live". The note will + * not be finally added to the layer until the corresponding + * note-off. + */ + void addNoteOn(sv_frame_t frame, int pitch, int velocity); + + /** + * Add a note-off. This will cause a note to appear, if and only + * if there is a matching pending note-on. + */ + void addNoteOff(sv_frame_t frame, int pitch); + + /** + * Abandon all pending note-on events. + */ + void abandonNoteOns(); + + virtual void toXml(QTextStream &stream, QString indent = "", + QString extraAttributes = "") const; + + void setProperties(const QXmlAttributes &attributes); + + void setVerticalRangeToNoteRange(LayerGeometryProvider *v); + + /// VerticalScaleLayer methods + virtual int getYForValue(LayerGeometryProvider *v, double value) const; + virtual double getValueForY(LayerGeometryProvider *v, int y) const; + virtual QString getScaleUnits() const; + +signals: + void reAnalyseRegion(sv_frame_t, sv_frame_t, float, float); + void materialiseReAnalysis(); + +protected: + void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const; + bool shouldConvertMIDIToHz() const; + + virtual int getDefaultColourHint(bool dark, bool &impose); + + FlexiNoteModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const; + + bool getPointToDrag(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &) const; + bool getNoteToEdit(LayerGeometryProvider *v, int x, int y, FlexiNoteModel::Point &) const; + void getRelativeMousePosition(LayerGeometryProvider *v, FlexiNoteModel::Point ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const; + SparseTimeValueModel *getAssociatedPitchModel(LayerGeometryProvider *v) const; + bool updateNoteValueFromPitchCurve(LayerGeometryProvider *v, FlexiNoteModel::Point ¬e) const; + void splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame, QMouseEvent *e); + + FlexiNoteModel *m_model; + bool m_editing; + bool m_intelligentActions; + int m_dragPointX; + int m_dragPointY; + int m_dragStartX; + int m_dragStartY; + FlexiNoteModel::Point m_originalPoint; + FlexiNoteModel::Point m_editingPoint; + sv_frame_t m_greatestLeftNeighbourFrame; + sv_frame_t m_smallestRightNeighbourFrame; + FlexiNoteModel::EditCommand *m_editingCommand; + VerticalScale m_verticalScale; + EditMode m_editMode; + + typedef std::set FlexiNoteSet; + FlexiNoteSet m_pendingNoteOns; + + mutable double m_scaleMinimum; + mutable double m_scaleMaximum; + + bool shouldAutoAlign() const; + + void finish(FlexiNoteModel::EditCommand *command) { + Command *c = command->finish(); + if (c) CommandHistory::getInstance()->addCommand(c, false); + } +}; + +#endif + diff -r 282f4be8f058 -r c02c51ae5238 layer/ImageLayer.cpp --- a/layer/ImageLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/ImageLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -78,7 +78,7 @@ } QString -ImageLayer::getPropertyLabel(const PropertyName &name) const +ImageLayer::getPropertyLabel(const PropertyName &) const { return ""; } @@ -110,20 +110,20 @@ } bool -ImageLayer::getValueExtents(float &, float &, bool &, QString &) const +ImageLayer::getValueExtents(double &, double &, bool &, QString &) const { return false; } bool -ImageLayer::isLayerScrollable(const View *v) const +ImageLayer::isLayerScrollable(const LayerGeometryProvider *) const { return true; } ImageModel::PointList -ImageLayer::getLocalPoints(View *v, int x, int y) const +ImageLayer::getLocalPoints(LayerGeometryProvider *v, int x, int ) const { if (!m_model) return ImageModel::PointList(); @@ -169,7 +169,7 @@ } QString -ImageLayer::getFeatureDescription(View *v, QPoint &pos) const +ImageLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); @@ -185,9 +185,9 @@ } } - long useFrame = points.begin()->frame; +// int useFrame = points.begin()->frame; - RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); +// RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); QString text; /* @@ -208,8 +208,8 @@ //!!! too much overlap with TimeValueLayer/TimeInstantLayer/TextLayer bool -ImageLayer::snapToFeatureFrame(View *v, int &frame, - size_t &resolution, +ImageLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -228,7 +228,7 @@ } points = m_model->getPoints(frame, frame); - int snapped = frame; + sv_frame_t snapped = frame; bool found = false; for (ImageModel::PointList::const_iterator i = points.begin(); @@ -280,26 +280,26 @@ } void -ImageLayer::paint(View *v, QPainter &paint, QRect rect) const +ImageLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) return; - int sampleRate = m_model->getSampleRate(); + sv_samplerate_t sampleRate = m_model->getSampleRate(); if (!sampleRate) return; // Profiler profiler("ImageLayer::paint", true); // int x0 = rect.left(), x1 = rect.right(); - int x0 = 0, x1 = v->width(); + int x0 = 0, x1 = v->getPaintWidth(); - long frame0 = v->getFrameForX(x0); - long frame1 = v->getFrameForX(x1); + sv_frame_t frame0 = v->getFrameForX(x0); + sv_frame_t frame1 = v->getFrameForX(x1); ImageModel::PointList points(m_model->getPoints(frame0, frame1)); if (points.empty()) return; paint.save(); - paint.setClipRect(rect.x(), 0, rect.width(), v->height()); + paint.setClipRect(rect.x(), 0, rect.width(), v->getPaintHeight()); QColor penColour; penColour = v->getForeground(); @@ -338,7 +338,7 @@ } void -ImageLayer::drawImage(View *v, QPainter &paint, const ImageModel::Point &p, +ImageLayer::drawImage(LayerGeometryProvider *v, QPainter &paint, const ImageModel::Point &p, int x, int nx) const { QString label = p.label; @@ -358,12 +358,12 @@ int bottomMargin = 10; int spacing = 5; - if (v->height() < 100) { + if (v->getPaintHeight() < 100) { topMargin = 5; bottomMargin = 5; } - int maxBoxHeight = v->height() - topMargin - bottomMargin; + int maxBoxHeight = v->getPaintHeight() - topMargin - bottomMargin; int availableWidth = nx - x - 3; if (availableWidth < 20) availableWidth = 20; @@ -372,7 +372,7 @@ if (label != "") { - int likelyHeight = v->height() / 4; + int likelyHeight = v->getPaintHeight() / 4; int likelyWidth = // available height times image aspect ((maxBoxHeight - likelyHeight) * imageSize.width()) @@ -435,10 +435,10 @@ division += paint.fontMetrics().height(); } - bottomMargin = v->height() - topMargin - boxHeight; - if (bottomMargin > topMargin + v->height()/7) { - topMargin += v->height()/8; - bottomMargin -= v->height()/8; + bottomMargin = v->getPaintHeight() - topMargin - boxHeight; + if (bottomMargin > topMargin + v->getPaintHeight()/7) { + topMargin += v->getPaintHeight()/8; + bottomMargin -= v->getPaintHeight()/8; } paint.drawRect(x - 1, @@ -480,7 +480,7 @@ } void -ImageLayer::setLayerDormant(const View *v, bool dormant) +ImageLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant) { if (dormant) { // Delete the images named in the view's scaled map from the @@ -517,10 +517,8 @@ } QImage -ImageLayer::getImage(View *v, QString name, QSize maxSize) const +ImageLayer::getImage(LayerGeometryProvider *v, QString name, QSize maxSize) const { - bool need = false; - // SVDEBUG << "ImageLayer::getImage(" << v << ", " << name << ", (" // << maxSize.width() << "x" << maxSize.height() << "))" << endl; @@ -556,7 +554,7 @@ } void -ImageLayer::drawStart(View *v, QMouseEvent *e) +ImageLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "ImageLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; @@ -565,7 +563,7 @@ return; } - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); @@ -580,13 +578,13 @@ } void -ImageLayer::drawDrag(View *v, QMouseEvent *e) +ImageLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "ImageLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); @@ -596,13 +594,11 @@ } void -ImageLayer::drawEnd(View *v, QMouseEvent *) +ImageLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *) { // SVDEBUG << "ImageLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; - bool ok = false; - ImageDialog dialog(tr("Select image"), "", ""); if (dialog.exec() == QDialog::Accepted) { @@ -623,7 +619,7 @@ } bool -ImageLayer::addImage(long frame, QString url) +ImageLayer::addImage(sv_frame_t frame, QString url) { QImage image(getLocalFilename(url)); if (image.isNull()) { @@ -642,7 +638,7 @@ } void -ImageLayer::editStart(View *v, QMouseEvent *e) +ImageLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "ImageLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; @@ -664,12 +660,12 @@ } void -ImageLayer::editDrag(View *v, QMouseEvent *e) +ImageLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; - long frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); - long frame = m_originalPoint.frame + frameDiff; + sv_frame_t frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); + sv_frame_t frame = m_originalPoint.frame + frameDiff; if (frame < 0) frame = 0; frame = (frame / m_model->getResolution()) * m_model->getResolution(); @@ -684,7 +680,7 @@ } void -ImageLayer::editEnd(View *, QMouseEvent *) +ImageLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) { // SVDEBUG << "ImageLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; @@ -698,7 +694,7 @@ } bool -ImageLayer::editOpen(View *v, QMouseEvent *e) +ImageLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return false; @@ -727,7 +723,7 @@ } void -ImageLayer::moveSelection(Selection s, size_t newStartFrame) +ImageLayer::moveSelection(Selection s, sv_frame_t newStartFrame) { if (!m_model) return; @@ -771,9 +767,9 @@ if (s.contains(i->frame)) { - double target = i->frame; - target = newSize.getStartFrame() + - double(target - s.getStartFrame()) * ratio; + double target = double(i->frame); + target = double(newSize.getStartFrame()) + + target - double(s.getStartFrame()) * ratio; ImageModel::Point newPoint(*i); newPoint.frame = lrint(target); @@ -805,7 +801,7 @@ } void -ImageLayer::copy(View *v, Selection s, Clipboard &to) +ImageLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -823,7 +819,7 @@ } bool -ImageLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */) +ImageLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */) { if (!m_model) return false; @@ -834,7 +830,7 @@ if (clipboardHasDifferentAlignment(v, from)) { QMessageBox::StandardButton button = - QMessageBox::question(v, tr("Re-align pasted items?"), + QMessageBox::question(v->getView(), tr("Re-align pasted items?"), tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); @@ -856,7 +852,7 @@ if (!i->haveFrame()) continue; - size_t frame = 0; + sv_frame_t frame = 0; if (!realign) { @@ -970,7 +966,7 @@ } void -ImageLayer::setProperties(const QXmlAttributes &attributes) +ImageLayer::setProperties(const QXmlAttributes &) { } diff -r 282f4be8f058 -r c02c51ae5238 layer/ImageLayer.h --- a/layer/ImageLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/ImageLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -38,31 +38,31 @@ ImageLayer(); virtual ~ImageLayer(); - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; - virtual bool snapToFeatureFrame(View *v, int &frame, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual void drawStart(View *v, QMouseEvent *); - virtual void drawDrag(View *v, QMouseEvent *); - virtual void drawEnd(View *v, QMouseEvent *); + virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void editStart(View *v, QMouseEvent *); - virtual void editDrag(View *v, QMouseEvent *); - virtual void editEnd(View *v, QMouseEvent *); + virtual void editStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void moveSelection(Selection s, size_t newStartFrame); + virtual void moveSelection(Selection s, sv_frame_t newStartFrame); virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(View *v, Selection s, Clipboard &to); - virtual bool paste(View *v, const Clipboard &from, int frameOffset, + virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to); + virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive); - virtual bool editOpen(View *, QMouseEvent *); // on double-click + virtual bool editOpen(LayerGeometryProvider *, QMouseEvent *); // on double-click virtual const Model *getModel() const { return m_model; } void setModel(ImageModel *model); @@ -80,43 +80,43 @@ return ColourAbsent; } - virtual bool isLayerScrollable(const View *v) const; + virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; virtual bool isLayerEditable() const { return true; } - virtual int getCompletion(View *) const { return m_model->getCompletion(); } + virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); } - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const; virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; - virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; } + virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; } - virtual void setLayerDormant(const View *v, bool dormant); + virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant); void setProperties(const QXmlAttributes &attributes); - virtual bool addImage(long frame, QString url); // using a command + virtual bool addImage(sv_frame_t frame, QString url); // using a command protected slots: void checkAddSources(); void fileSourceReady(); protected: - ImageModel::PointList getLocalPoints(View *v, int x, int y) const; + ImageModel::PointList getLocalPoints(LayerGeometryProvider *v, int x, int y) const; bool getImageOriginalSize(QString name, QSize &size) const; - QImage getImage(View *v, QString name, QSize maxSize) const; + QImage getImage(LayerGeometryProvider *v, QString name, QSize maxSize) const; - void drawImage(View *v, QPainter &paint, const ImageModel::Point &p, + void drawImage(LayerGeometryProvider *v, QPainter &paint, const ImageModel::Point &p, int x, int nx) const; //!!! how to reap no-longer-used images? typedef std::map ImageMap; - typedef std::map ViewImageMap; + typedef std::map ViewImageMap; typedef std::map FileSourceMap; static ImageMap m_images; diff -r 282f4be8f058 -r c02c51ae5238 layer/ImageRegionFinder.cpp --- a/layer/ImageRegionFinder.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/ImageRegionFinder.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -107,9 +107,9 @@ return false; } - float ar = float(qRed(a) / 255.f); - float ag = float(qGreen(a) / 255.f); - float ab = float(qBlue(a) / 255.f); + float ar = float(qRed(a)) / 255.f; + float ag = float(qGreen(a)) / 255.f; + float ab = float(qBlue(a)) / 255.f; float amag = sqrtf(ar * ar + ag * ag + ab * ab); float thresh = amag / 2; diff -r 282f4be8f058 -r c02c51ae5238 layer/Layer.cpp --- a/layer/Layer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/Layer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -51,8 +51,8 @@ connect(model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); - connect(model, SIGNAL(modelChanged(size_t, size_t)), - this, SIGNAL(modelChanged(size_t, size_t))); + connect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), + this, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t))); connect(model, SIGNAL(completionChanged()), this, SIGNAL(modelCompletionChanged())); @@ -115,7 +115,7 @@ } void -Layer::setLayerDormant(const View *v, bool dormant) +Layer::setLayerDormant(const LayerGeometryProvider *v, bool dormant) { const void *vv = (const void *)v; QMutexLocker locker(&m_dormancyMutex); @@ -123,7 +123,7 @@ } bool -Layer::isLayerDormant(const View *v) const +Layer::isLayerDormant(const LayerGeometryProvider *v) const { const void *vv = (const void *)v; QMutexLocker locker(&m_dormancyMutex); @@ -132,65 +132,65 @@ } void -Layer::showLayer(View *view, bool show) +Layer::showLayer(LayerGeometryProvider *view, bool show) { setLayerDormant(view, !show); emit layerParametersChanged(); } bool -Layer::getXScaleValue(const View *v, int x, float &value, QString &unit) const +Layer::getXScaleValue(const LayerGeometryProvider *v, int x, double &value, QString &unit) const { if (!hasTimeXAxis()) return false; const Model *m = getModel(); if (!m) return false; - value = float(v->getFrameForX(x)) / m->getSampleRate(); + value = double(v->getFrameForX(x)) / m->getSampleRate(); unit = "s"; return true; } bool -Layer::getYScaleDifference(const View *v, int y0, int y1, - float &diff, QString &unit) const +Layer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1, + double &diff, QString &unit) const { - float v0, v1; + double v0, v1; if (!getYScaleValue(v, y0, v0, unit) || !getYScaleValue(v, y1, v1, unit)) { diff = 0.f; return false; } - diff = fabsf(v1 - v0); + diff = fabs(v1 - v0); return true; } -size_t -Layer::alignToReference(View *v, size_t frame) const +sv_frame_t +Layer::alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const { const Model *m = getModel(); SVDEBUG << "Layer::alignToReference(" << frame << "): model = " << m << ", alignment reference = " << (m ? m->getAlignmentReference() : 0) << endl; if (m && m->getAlignmentReference()) { return m->alignToReference(frame); } else { - return v->alignToReference(frame); + return v->getView()->alignToReference(frame); } } -size_t -Layer::alignFromReference(View *v, size_t frame) const +sv_frame_t +Layer::alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const { const Model *m = getModel(); SVDEBUG << "Layer::alignFromReference(" << frame << "): model = " << m << ", alignment reference = " << (m ? m->getAlignmentReference() : 0) << endl; if (m && m->getAlignmentReference()) { return m->alignFromReference(frame); } else { - return v->alignFromReference(frame); + return v->getView()->alignFromReference(frame); } } bool -Layer::clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const +Layer::clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const { // Notes on pasting to an aligned layer: // @@ -250,12 +250,12 @@ // reference (i.e. having been copied from the reference // model). - long sourceFrame = i->getFrame(); - long referenceFrame = sourceFrame; + sv_frame_t sourceFrame = i->getFrame(); + sv_frame_t referenceFrame = sourceFrame; if (i->haveReferenceFrame()) { referenceFrame = i->getReferenceFrame(); } - long myMappedFrame = alignToReference(v, sourceFrame); + sv_frame_t myMappedFrame = alignToReference(v, sourceFrame); // cerr << "sourceFrame = " << sourceFrame << ", referenceFrame = " << referenceFrame << " (have = " << i->haveReferenceFrame() << "), myMappedFrame = " << myMappedFrame << endl; @@ -320,8 +320,8 @@ QString fs = attributes.value("startFrame"); int x0 = 0, x1 = 0; if (fs != "") { - rect.startFrame = fs.toLong(); - rect.endFrame = attributes.value("endFrame").toLong(); + rect.startFrame = fs.toInt(); + rect.endFrame = attributes.value("endFrame").toInt(); rect.haveFrames = true; } else { x0 = attributes.value("startX").toInt(); @@ -371,7 +371,7 @@ } void -Layer::measureStart(View *v, QMouseEvent *e) +Layer::measureStart(LayerGeometryProvider *v, QMouseEvent *e) { setMeasureRectFromPixrect(v, m_draggingRect, QRect(e->x(), e->y(), 0, 0)); @@ -379,7 +379,7 @@ } void -Layer::measureDrag(View *v, QMouseEvent *e) +Layer::measureDrag(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_haveDraggingRect) return; @@ -391,7 +391,7 @@ } void -Layer::measureEnd(View *v, QMouseEvent *e) +Layer::measureEnd(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_haveDraggingRect) return; measureDrag(v, e); @@ -405,7 +405,7 @@ } void -Layer::measureDoubleClick(View *v, QMouseEvent *e) +Layer::measureDoubleClick(LayerGeometryProvider *, QMouseEvent *) { // nothing, in the base class } @@ -425,7 +425,7 @@ } void -Layer::paintMeasurementRects(View *v, QPainter &paint, +Layer::paintMeasurementRects(LayerGeometryProvider *v, QPainter &paint, bool showFocus, QPoint focusPoint) const { updateMeasurePixrects(v); @@ -457,7 +457,7 @@ } bool -Layer::nearestMeasurementRectChanged(View *v, QPoint prev, QPoint now) const +Layer::nearestMeasurementRectChanged(LayerGeometryProvider *v, QPoint prev, QPoint now) const { updateMeasurePixrects(v); @@ -468,10 +468,10 @@ } void -Layer::updateMeasurePixrects(View *v) const +Layer::updateMeasurePixrects(LayerGeometryProvider *v) const { - long sf = v->getStartFrame(); - long ef = v->getEndFrame(); + sv_frame_t sf = v->getStartFrame(); + sv_frame_t ef = v->getEndFrame(); for (MeasureRectSet::const_iterator i = m_measureRects.begin(); i != m_measureRects.end(); ++i) { @@ -495,7 +495,7 @@ if (i->startFrame >= v->getStartFrame()) { x0 = v->getXForFrame(i->startFrame); } - if (i->endFrame <= long(v->getEndFrame())) { + if (i->endFrame <= int(v->getEndFrame())) { x1 = v->getXForFrame(i->endFrame); } } @@ -507,26 +507,26 @@ } void -Layer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const +Layer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const { - int y0 = lrint(r.startY * v->height()); - int y1 = lrint(r.endY * v->height()); + int y0 = int(lrint(r.startY * v->getPaintHeight())); + int y1 = int(lrint(r.endY * v->getPaintHeight())); r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0); } void -Layer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const +Layer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const { if (start) { - r.startY = double(y) / double(v->height()); + r.startY = double(y) / double(v->getPaintHeight()); r.endY = r.startY; } else { - r.endY = double(y) / double(v->height()); + r.endY = double(y) / double(v->getPaintHeight()); } } void -Layer::setMeasureRectFromPixrect(View *v, MeasureRect &r, QRect pixrect) const +Layer::setMeasureRectFromPixrect(LayerGeometryProvider *v, MeasureRect &r, QRect pixrect) const { r.pixrect = pixrect; r.haveFrames = hasTimeXAxis(); @@ -541,7 +541,7 @@ Layer::MeasureRectSet::const_iterator Layer::findFocusedMeasureRect(QPoint focusPoint) const { - float frDist = 0; + double frDist = 0; MeasureRectSet::const_iterator focusRectItr = m_measureRects.end(); for (MeasureRectSet::const_iterator i = m_measureRects.begin(); @@ -554,7 +554,7 @@ int xd = focusPoint.x() - cx; int yd = focusPoint.y() - cy; - float d = sqrt(float(xd * xd + yd * yd)); + double d = sqrt(double(xd * xd + yd * yd)); if (focusRectItr == m_measureRects.end() || d < frDist) { focusRectItr = i; @@ -566,18 +566,18 @@ } void -Layer::paintMeasurementRect(View *v, QPainter &paint, +Layer::paintMeasurementRect(LayerGeometryProvider *v, QPainter &paint, const MeasureRect &r, bool focus) const { if (r.haveFrames) { int x0 = -1; - int x1 = v->width() + 1; + int x1 = v->getPaintWidth() + 1; if (r.startFrame >= v->getStartFrame()) { x0 = v->getXForFrame(r.startFrame); } - if (r.endFrame <= long(v->getEndFrame())) { + if (r.endFrame <= v->getEndFrame()) { x1 = v->getXForFrame(r.endFrame); } diff -r 282f4be8f058 -r c02c51ae5238 layer/Layer.h --- a/layer/Layer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/Layer.h Wed Apr 20 12:06:28 2016 +0100 @@ -39,6 +39,7 @@ class Model; class QPainter; class View; +class LayerGeometryProvider; class QMouseEvent; class Clipboard; class RangeMapper; @@ -62,7 +63,7 @@ Model *getModel() { return const_cast(const_cast(this)->getModel()); } - + /** * Return a zoom constraint object defining the supported zoom * levels for this layer. If this returns zero, the layer will @@ -83,12 +84,13 @@ /** * Paint the given rectangle of this layer onto the given view * using the given painter, superimposing it on top of any - * existing material in that view. The view is provided here - * because it is possible for one layer to exist in more than one - * view, so the dimensions of the view may vary from one paint - * call to another (without any view having been resized). + * existing material in that view. The LayerGeometryProvider (an + * interface implemented by View) is provided here because it is + * possible for one layer to exist in more than one view, so the + * dimensions of the view may vary from one paint call to another + * (without any view having been resized). */ - virtual void paint(View *, QPainter &, QRect) const = 0; + virtual void paint(LayerGeometryProvider *, QPainter &, QRect) const = 0; /** * Enable or disable synchronous painting. If synchronous @@ -128,29 +130,29 @@ virtual QString getLayerPresentationName() const; virtual QPixmap getLayerPresentationPixmap(QSize) const { return QPixmap(); } - virtual int getVerticalScaleWidth(View *, bool detailed, + virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool detailed, QPainter &) const = 0; - virtual void paintVerticalScale(View *, bool detailed, + virtual void paintVerticalScale(LayerGeometryProvider *, bool /* detailed */, QPainter &, QRect) const { } - virtual bool getCrosshairExtents(View *, QPainter &, QPoint /* cursorPos */, + virtual bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint /* cursorPos */, std::vector &) const { return false; } - virtual void paintCrosshairs(View *, QPainter &, QPoint) const { } + virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const { } - virtual void paintMeasurementRects(View *, QPainter &, + virtual void paintMeasurementRects(LayerGeometryProvider *, QPainter &, bool showFocus, QPoint focusPoint) const; - virtual bool nearestMeasurementRectChanged(View *, QPoint prev, + virtual bool nearestMeasurementRectChanged(LayerGeometryProvider *, QPoint prev, QPoint now) const; - virtual QString getFeatureDescription(View *, QPoint &) const { + virtual QString getFeatureDescription(LayerGeometryProvider *, QPoint &) const { return ""; } - virtual QString getLabelPreceding(size_t frame) const { + virtual QString getLabelPreceding(sv_frame_t /* frame */) const { return ""; } @@ -180,9 +182,9 @@ * (and leave frame unmodified). If returning true, also return * the resolution of the model in this layer in sample frames. */ - virtual bool snapToFeatureFrame(View * /* v */, - int & /* frame */, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider * /* v */, + sv_frame_t & /* frame */, + int &resolution, SnapType /* snap */) const { resolution = 1; return false; @@ -204,9 +206,9 @@ * (and leave frame unmodified). If returning true, also return * the resolution of the model in this layer in sample frames. */ - virtual bool snapToSimilarFeature(View * /* v */, - int & /* source frame */, - size_t &resolution, + virtual bool snapToSimilarFeature(LayerGeometryProvider * /* v */, + sv_frame_t & /* source frame */, + int &resolution, SnapType /* snap */) const { resolution = 1; return false; @@ -217,25 +219,30 @@ // Layer needs to get actual mouse events, I guess. Draw mode is // probably the easier. - virtual void drawStart(View *, QMouseEvent *) { } - virtual void drawDrag(View *, QMouseEvent *) { } - virtual void drawEnd(View *, QMouseEvent *) { } - virtual void eraseStart(View *, QMouseEvent *) { } - virtual void eraseDrag(View *, QMouseEvent *) { } - virtual void eraseEnd(View *, QMouseEvent *) { } + virtual void drawStart(LayerGeometryProvider *, QMouseEvent *) { } + virtual void drawDrag(LayerGeometryProvider *, QMouseEvent *) { } + virtual void drawEnd(LayerGeometryProvider *, QMouseEvent *) { } - virtual void editStart(View *, QMouseEvent *) { } - virtual void editDrag(View *, QMouseEvent *) { } - virtual void editEnd(View *, QMouseEvent *) { } + virtual void eraseStart(LayerGeometryProvider *, QMouseEvent *) { } + virtual void eraseDrag(LayerGeometryProvider *, QMouseEvent *) { } + virtual void eraseEnd(LayerGeometryProvider *, QMouseEvent *) { } + + virtual void editStart(LayerGeometryProvider *, QMouseEvent *) { } + virtual void editDrag(LayerGeometryProvider *, QMouseEvent *) { } + virtual void editEnd(LayerGeometryProvider *, QMouseEvent *) { } + + virtual void splitStart(LayerGeometryProvider *, QMouseEvent *) { } + virtual void splitEnd(LayerGeometryProvider *, QMouseEvent *) { } + virtual void addNote(LayerGeometryProvider *, QMouseEvent *) { }; // Measurement rectangle (or equivalent). Unlike draw and edit, // the base Layer class can provide working implementations of // these for most situations. // - virtual void measureStart(View *, QMouseEvent *); - virtual void measureDrag(View *, QMouseEvent *); - virtual void measureEnd(View *, QMouseEvent *); - virtual void measureDoubleClick(View *, QMouseEvent *); + virtual void measureStart(LayerGeometryProvider *, QMouseEvent *); + virtual void measureDrag(LayerGeometryProvider *, QMouseEvent *); + virtual void measureEnd(LayerGeometryProvider *, QMouseEvent *); + virtual void measureDoubleClick(LayerGeometryProvider *, QMouseEvent *); virtual bool haveCurrentMeasureRect() const { return m_haveCurrentMeasureRect; @@ -247,13 +254,13 @@ * double-click). If there is no item or editing is not * supported, return false. */ - virtual bool editOpen(View *, QMouseEvent *) { return false; } + virtual bool editOpen(LayerGeometryProvider *, QMouseEvent *) { return false; } - virtual void moveSelection(Selection, size_t /* newStartFrame */) { } + virtual void moveSelection(Selection, sv_frame_t /* newStartFrame */) { } virtual void resizeSelection(Selection, Selection /* newSize */) { } virtual void deleteSelection(Selection) { } - virtual void copy(View *, Selection, Clipboard & /* to */) { } + virtual void copy(LayerGeometryProvider *, Selection, Clipboard & /* to */) { } /** * Paste from the given clipboard onto the layer at the given @@ -262,9 +269,9 @@ * return false if the user cancelled the paste operation. This * function should return true if a paste actually occurred. */ - virtual bool paste(View *, + virtual bool paste(LayerGeometryProvider *, const Clipboard & /* from */, - int /* frameOffset */, + sv_frame_t /* frameOffset */, bool /* interactive */) { return false; } // Text mode: @@ -284,7 +291,7 @@ * scrolling better if it is known that individual views can be * scrolled safely in this way. */ - virtual bool isLayerScrollable(const View *) const { return true; } + virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return true; } /** * This should return true if the layer completely obscures any @@ -339,14 +346,14 @@ * isReady(int *) call. The view may choose to show a progress * meter if it finds that this returns < 100 at any given moment. */ - virtual int getCompletion(View *) const { return 100; } + virtual int getCompletion(LayerGeometryProvider *) const { return 100; } /** * Return an error string if any errors have occurred while * loading or processing data for the given view. Return the * empty string if no error has occurred. */ - virtual QString getError(View *) const { return ""; } + virtual QString getError(LayerGeometryProvider *) const { return ""; } virtual void setObjectName(const QString &name); @@ -395,13 +402,13 @@ * A layer class that overrides this function must also call this * class's implementation. */ - virtual void setLayerDormant(const View *v, bool dormant); + virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant); /** * Return whether the layer is dormant (i.e. hidden) in the given * view. */ - virtual bool isLayerDormant(const View *v) const; + virtual bool isLayerDormant(const LayerGeometryProvider *v) const; virtual PlayParameters *getPlayParameters(); @@ -418,7 +425,7 @@ * This function returns the "normal" extents for the layer, not * necessarily the extents actually in use in the display. */ - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const = 0; /** @@ -429,8 +436,8 @@ * extent (using the normal layer extents or deferring to whatever * is in use for the same units elsewhere in the view). */ - virtual bool getDisplayExtents(float & /* min */, - float & /* max */) const { + virtual bool getDisplayExtents(double & /* min */, + double & /* max */) const { return false; } @@ -441,8 +448,8 @@ * return false for getDisplayExtents should also return false for * this function. */ - virtual bool setDisplayExtents(float /* min */, - float /* max */) { + virtual bool setDisplayExtents(double /* min */, + double /* max */) { return false; } @@ -452,15 +459,15 @@ * measurement tool. The default implementation works correctly * if the layer hasTimeXAxis(). */ - virtual bool getXScaleValue(const View *v, int x, - float &value, QString &unit) const; + virtual bool getXScaleValue(const LayerGeometryProvider *v, int x, + double &value, QString &unit) const; /** * Return the value and unit at the given y coordinate in the * given view. */ - virtual bool getYScaleValue(const View *, int /* y */, - float &/* value */, QString &/* unit */) const { + virtual bool getYScaleValue(const LayerGeometryProvider *, int /* y */, + double &/* value */, QString &/* unit */) const { return false; } @@ -470,8 +477,8 @@ * The default implementation just calls getYScaleValue twice and * returns the difference, with the same unit. */ - virtual bool getYScaleDifference(const View *v, int y0, int y1, - float &diff, QString &unit) const; + virtual bool getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1, + double &diff, QString &unit) const; /** * Get the number of vertical zoom steps available for this layer. @@ -513,14 +520,21 @@ */ virtual RangeMapper *getNewVerticalZoomRangeMapper() const { return 0; } + /** + * Return true if this layer type can function without a model + * being set. If false (the default), the layer will not be loaded + * from a session if its model cannot be found. + */ + virtual bool canExistWithoutModel() const { return false; } + public slots: - void showLayer(View *, bool show); + void showLayer(LayerGeometryProvider *, bool show); signals: void modelChanged(); void modelCompletionChanged(); void modelAlignmentCompletionChanged(); - void modelChanged(size_t startFrame, size_t endFrame); + void modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame); void modelReplaced(); void layerParametersChanged(); @@ -533,16 +547,16 @@ protected: void connectSignals(const Model *); - virtual size_t alignToReference(View *v, size_t frame) const; - virtual size_t alignFromReference(View *v, size_t frame) const; - bool clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const; + virtual sv_frame_t alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const; + virtual sv_frame_t alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const; + bool clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const; struct MeasureRect { mutable QRect pixrect; bool haveFrames; - long startFrame; // only valid if haveFrames - long endFrame; // ditto + sv_frame_t startFrame; // only valid if haveFrames + sv_frame_t endFrame; // ditto double startY; double endY; @@ -600,16 +614,16 @@ // Note that pixrects are only correct for a single view. // So we should update them at the start of the paint procedure // (painting is single threaded) and only use them after that. - void updateMeasurePixrects(View *v) const; + void updateMeasurePixrects(LayerGeometryProvider *v) const; - virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const; - virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const; - virtual void setMeasureRectFromPixrect(View *v, MeasureRect &r, QRect pixrect) const; + virtual void updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const; + virtual void setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const; + virtual void setMeasureRectFromPixrect(LayerGeometryProvider *v, MeasureRect &r, QRect pixrect) const; // This assumes updateMeasurementPixrects has been called MeasureRectSet::const_iterator findFocusedMeasureRect(QPoint) const; - void paintMeasurementRect(View *v, QPainter &paint, + void paintMeasurementRect(LayerGeometryProvider *v, QPainter &paint, const MeasureRect &r, bool focus) const; QString m_presentationName; diff -r 282f4be8f058 -r c02c51ae5238 layer/LayerFactory.cpp --- a/layer/LayerFactory.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LayerFactory.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -21,6 +21,7 @@ #include "TimeInstantLayer.h" #include "TimeValueLayer.h" #include "NoteLayer.h" +#include "FlexiNoteLayer.h" #include "RegionLayer.h" #include "TextLayer.h" #include "ImageLayer.h" @@ -36,6 +37,7 @@ #include "data/model/SparseOneDimensionalModel.h" #include "data/model/SparseTimeValueModel.h" #include "data/model/NoteModel.h" +#include "data/model/FlexiNoteModel.h" #include "data/model/RegionModel.h" #include "data/model/TextModel.h" #include "data/model/ImageModel.h" @@ -73,6 +75,7 @@ case TimeInstants: return Layer::tr("Time Instants"); case TimeValues: return Layer::tr("Time Values"); case Notes: return Layer::tr("Notes"); + case FlexiNotes: return Layer::tr("Flexible Notes"); case Regions: return Layer::tr("Regions"); case Text: return Layer::tr("Text"); case Image: return Layer::tr("Images"); @@ -90,10 +93,11 @@ // likewise return Layer::tr("Spectrogram"); - default: break; + case UnknownLayer: + default: + cerr << "WARNING: LayerFactory::getLayerPresentationName passed unknown layer" << endl; + return Layer::tr("Unknown Layer"); } - - return Layer::tr("Layer"); } bool @@ -161,6 +165,11 @@ types.insert(Notes); } + // NOTE: GF: types is a set, so order of insertion does not matter + if (dynamic_cast(model)) { + types.insert(FlexiNotes); + } + if (dynamic_cast(model)) { types.insert(Regions); } @@ -189,6 +198,10 @@ LayerTypeSet types; types.insert(TimeInstants); types.insert(TimeValues); + // Because this is strictly a UI function -- list the layer types + // to show in a menu -- it should not contain FlexiNotes; the + // layer isn't meaningfully editable in SV +// types.insert(FlexiNotes); types.insert(Notes); types.insert(Regions); types.insert(Text); @@ -205,6 +218,7 @@ if (dynamic_cast(layer)) return TimeRuler; if (dynamic_cast(layer)) return TimeInstants; if (dynamic_cast(layer)) return TimeValues; + if (dynamic_cast(layer)) return FlexiNotes; if (dynamic_cast(layer)) return Notes; if (dynamic_cast(layer)) return Regions; if (dynamic_cast(layer)) return Text; @@ -225,6 +239,7 @@ case TimeInstants: return "instants"; case TimeValues: return "values"; case Notes: return "notes"; + case FlexiNotes: return "flexinotes"; case Regions: return "regions"; case Text: return "text"; case Image: return "image"; @@ -233,7 +248,10 @@ case Slice: return "spectrum"; case MelodicRangeSpectrogram: return "spectrogram"; case PeakFrequencySpectrogram: return "spectrogram"; - default: return "unknown"; + case UnknownLayer: + default: + cerr << "WARNING: LayerFactory::getLayerIconName passed unknown layer" << endl; + return "unknown"; } } @@ -247,6 +265,7 @@ case TimeInstants: return "timeinstants"; case TimeValues: return "timevalues"; case Notes: return "notes"; + case FlexiNotes: return "flexinotes"; case Regions: return "regions"; case Text: return "text"; case Image: return "image"; @@ -255,7 +274,10 @@ case Slice: return "slice"; case MelodicRangeSpectrogram: return "melodicrange"; case PeakFrequencySpectrogram: return "peakfrequency"; - default: return "unknown"; + case UnknownLayer: + default: + cerr << "WARNING: LayerFactory::getLayerTypeName passed unknown layer" << endl; + return "unknown"; } } @@ -268,6 +290,7 @@ if (name == "timeinstants") return TimeInstants; if (name == "timevalues") return TimeValues; if (name == "notes") return Notes; + if (name == "flexinotes") return FlexiNotes; if (name == "regions") return Regions; if (name == "text") return Text; if (name == "image") return Image; @@ -282,7 +305,7 @@ { // if (trySetModel(layer, model)) // return; - + if (trySetModel(layer, model)) return; @@ -301,9 +324,13 @@ if (trySetModel(layer, model)) return; - if (trySetModel(layer, model)) - return; + if (trySetModel(layer, model)) + return; + // GF: added FlexiNoteLayer + if (trySetModel(layer, model)) + return; + if (trySetModel(layer, model)) return; @@ -333,6 +360,8 @@ return new SparseOneDimensionalModel(baseModel->getSampleRate(), 1); } else if (layerType == TimeValues) { return new SparseTimeValueModel(baseModel->getSampleRate(), 1, true); + } else if (layerType == FlexiNotes) { + return new FlexiNoteModel(baseModel->getSampleRate(), 1, true); } else if (layerType == Notes) { return new NoteModel(baseModel->getSampleRate(), 1, true); } else if (layerType == Regions) { @@ -402,6 +431,10 @@ layer = new TimeValueLayer; break; + case FlexiNotes: + layer = new FlexiNoteLayer; + break; + case Notes: layer = new NoteLayer; break; @@ -438,11 +471,14 @@ layer = new SpectrogramLayer(SpectrogramLayer::MelodicPeaks); break; - default: break; + case UnknownLayer: + default: + cerr << "WARNING: LayerFactory::createLayer passed unknown layer" << endl; + break; } if (!layer) { - SVDEBUG << "LayerFactory::createLayer: Unknown layer type " + cerr << "LayerFactory::createLayer: Unknown layer type " << type << endl; } else { // SVDEBUG << "LayerFactory::createLayer: Setting object name " @@ -477,7 +513,7 @@ QDomElement layerElt = docNew.firstChildElement("layer"); QDomNamedNodeMap attrNodes = layerElt.attributes(); - for (unsigned int i = 0; i < attrNodes.length(); ++i) { + for (int i = 0; i < attrNodes.length(); ++i) { QDomAttr attr = attrNodes.item(i).toAttr(); if (attr.isNull()) continue; // cerr << "append \"" << attr.name() @@ -488,7 +524,7 @@ layerElt = docOld.firstChildElement("layer"); attrNodes = layerElt.attributes(); - for (unsigned int i = 0; i < attrNodes.length(); ++i) { + for (int i = 0; i < attrNodes.length(); ++i) { QDomAttr attr = attrNodes.item(i).toAttr(); if (attr.isNull()) continue; if (attrs.value(attr.name()) == "") { diff -r 282f4be8f058 -r c02c51ae5238 layer/LayerFactory.h --- a/layer/LayerFactory.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LayerFactory.h Wed Apr 20 12:06:28 2016 +0100 @@ -35,6 +35,7 @@ TimeInstants, TimeValues, Notes, + FlexiNotes, Regions, Text, Image, @@ -56,6 +57,11 @@ typedef std::set LayerTypeSet; LayerTypeSet getValidLayerTypes(Model *model); + + /** + * Return the set of layer types that an end user should be + * allowed to create, empty, for subsequent editing. + */ LayerTypeSet getValidEmptyLayerTypes(); LayerType getLayerType(const Layer *); diff -r 282f4be8f058 -r c02c51ae5238 layer/LinearColourScale.cpp --- a/layer/LinearColourScale.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LinearColourScale.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -20,33 +20,32 @@ #include -#include "view/View.h" +#include "view/LayerGeometryProvider.h" int -LinearColourScale::getWidth(View *v, +LinearColourScale::getWidth(LayerGeometryProvider *, QPainter &paint) { return paint.fontMetrics().width("-000.00") + 15; } void -LinearColourScale::paintVertical(View *v, +LinearColourScale::paintVertical(LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, - int x0, - float min, - float max) + int /* x0 */, + double min, + double max) { - int h = v->height(); + int h = v->getPaintHeight(); int n = 10; - float val = min; - float inc = (max - val) / n; - - char buffer[40]; - - int w = getWidth(v, paint) + x0; + double val = min; + double inc = (max - val) / n; + + const int buflen = 40; + char buffer[buflen]; int boxx = 5, boxy = 5; if (layer->getScaleUnits() != "") { @@ -59,22 +58,22 @@ paint.save(); for (int y = 0; y < boxh; ++y) { - float val = ((boxh - y) * (max - min)) / boxh + min; + double val = ((boxh - y) * (max - min)) / boxh + min; paint.setPen(layer->getColourForValue(v, val)); paint.drawLine(boxx + 1, y + boxy + 1, boxx + boxw, y + boxy + 1); } paint.restore(); - float round = 1.f; +// double round = 1.f; int dp = 0; if (inc > 0) { - int prec = trunc(log10f(inc)); + int prec = int(trunc(log10(inc))); prec -= 1; if (prec < 0) dp = -prec; - round = powf(10.f, prec); -#ifdef DEBUG_TIME_VALUE_LAYER - cerr << "inc = " << inc << ", round = " << round << ", dp = " << dp << endl; -#endif +// round = powf(10.f, prec); +//#ifdef DEBUG_TIME_VALUE_LAYER +// cerr << "inc = " << inc << ", round = " << round << ", dp = " << dp << endl; +//#endif } for (int i = 0; i < n; ++i) { @@ -86,7 +85,7 @@ ty = y - paint.fontMetrics().height() + paint.fontMetrics().ascent() + 2; - sprintf(buffer, "%.*f", dp, val); + snprintf(buffer, buflen, "%.*f", dp, val); QString label = QString(buffer); paint.drawLine(boxx + boxw - boxw/3, y, boxx + boxw, y); diff -r 282f4be8f058 -r c02c51ae5238 layer/LinearColourScale.h --- a/layer/LinearColourScale.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LinearColourScale.h Wed Apr 20 12:06:28 2016 +0100 @@ -19,17 +19,17 @@ #include class QPainter; -class View; +class LayerGeometryProvider; class ColourScaleLayer; class LinearColourScale { public: - int getWidth(View *v, QPainter &paint); + int getWidth(LayerGeometryProvider *v, QPainter &paint); void paintVertical - (View *v, const ColourScaleLayer *layer, QPainter &paint, int x0, - float minf, float maxf); + (LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, int x0, + double minf, double maxf); }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/LinearNumericalScale.cpp --- a/layer/LinearNumericalScale.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LinearNumericalScale.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -23,38 +23,37 @@ #include "view/View.h" int -LinearNumericalScale::getWidth(View *v, +LinearNumericalScale::getWidth(LayerGeometryProvider *, QPainter &paint) { return paint.fontMetrics().width("-000.00") + 10; } void -LinearNumericalScale::paintVertical(View *v, +LinearNumericalScale::paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, - float minf, - float maxf) + double minf, + double maxf) { - int h = v->height(); - int n = 10; - float val = minf; - float inc = (maxf - val) / n; + double val = minf; + double inc = (maxf - val) / n; - char buffer[40]; + const int buflen = 40; + char buffer[buflen]; int w = getWidth(v, paint) + x0; - float round = 1.f; + double round = 1.0; int dp = 0; if (inc > 0) { - int prec = trunc(log10f(inc)); + int prec = int(trunc(log10(inc))); prec -= 1; if (prec < 0) dp = -prec; - round = powf(10.f, prec); + round = pow(10.0, prec); #ifdef DEBUG_TIME_VALUE_LAYER cerr << "inc = " << inc << ", round = " << round << ", dp = " << dp << endl; #endif @@ -67,13 +66,13 @@ int y, ty; bool drawText = true; - float dispval = val; + double dispval = val; if (i == n-1 && - v->height() < paint.fontMetrics().height() * (n*2)) { + v->getPaintHeight() < paint.fontMetrics().height() * (n*2)) { if (layer->getScaleUnits() != "") drawText = false; } - dispval = lrintf(val / round) * round; + dispval = int(rint(val / round) * round); #ifdef DEBUG_TIME_VALUE_LAYER cerr << "val = " << val << ", dispval = " << dispval << endl; @@ -88,7 +87,7 @@ continue; } - sprintf(buffer, "%.*f", dp, dispval); + snprintf(buffer, buflen, "%.*f", dp, dispval); QString label = QString(buffer); diff -r 282f4be8f058 -r c02c51ae5238 layer/LinearNumericalScale.h --- a/layer/LinearNumericalScale.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LinearNumericalScale.h Wed Apr 20 12:06:28 2016 +0100 @@ -19,17 +19,17 @@ #include class QPainter; -class View; +class LayerGeometryProvider; class VerticalScaleLayer; class LinearNumericalScale { public: - int getWidth(View *v, QPainter &paint); + int getWidth(LayerGeometryProvider *v, QPainter &paint); void paintVertical - (View *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, - float minf, float maxf); + (LayerGeometryProvider *v, const VerticalScaleLayer *layer, + QPainter &paint, int x0, double minf, double maxf); }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/LogColourScale.cpp --- a/layer/LogColourScale.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LogColourScale.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -25,30 +25,29 @@ #include "view/View.h" int -LogColourScale::getWidth(View *v, +LogColourScale::getWidth(LayerGeometryProvider *, QPainter &paint) { return paint.fontMetrics().width("-000.00") + 15; } void -LogColourScale::paintVertical(View *v, +LogColourScale::paintVertical(LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, - int x0, - float minlog, - float maxlog) + int /* x0 */, + double minlog, + double maxlog) { - int h = v->height(); + int h = v->getPaintHeight(); int n = 10; - float val = minlog; - float inc = (maxlog - val) / n; + double val = minlog; + double inc = (maxlog - val) / n; - char buffer[40]; - - int w = getWidth(v, paint) + x0; + const int buflen = 40; + char buffer[buflen]; int boxx = 5, boxy = 5; if (layer->getScaleUnits() != "") { @@ -61,7 +60,7 @@ paint.save(); for (int y = 0; y < boxh; ++y) { - float val = ((boxh - y) * (maxlog - minlog)) / boxh + minlog; + double val = ((boxh - y) * (maxlog - minlog)) / boxh + minlog; paint.setPen(layer->getColourForValue(v, LogRange::unmap(val))); paint.drawLine(boxx + 1, y + boxy + 1, boxx + boxw, y + boxy + 1); } @@ -69,7 +68,7 @@ int dp = 0; if (inc > 0) { - int prec = trunc(log10f(inc)); + int prec = int(trunc(log10(inc))); prec -= 1; if (prec < 0) dp = -prec; } @@ -84,10 +83,10 @@ paint.fontMetrics().ascent() + 2; double dv = LogRange::unmap(val); - int digits = trunc(log10f(dv)); + int digits = int(trunc(log10(dv))); int sf = dp + (digits > 0 ? digits : 0); if (sf < 2) sf = 2; - sprintf(buffer, "%.*g", sf, dv); + snprintf(buffer, buflen, "%.*g", sf, dv); QString label = QString(buffer); diff -r 282f4be8f058 -r c02c51ae5238 layer/LogColourScale.h --- a/layer/LogColourScale.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LogColourScale.h Wed Apr 20 12:06:28 2016 +0100 @@ -19,17 +19,17 @@ #include class QPainter; -class View; +class LayerGeometryProvider; class ColourScaleLayer; class LogColourScale { public: - int getWidth(View *v, QPainter &paint); + int getWidth(LayerGeometryProvider *v, QPainter &paint); void paintVertical - (View *v, const ColourScaleLayer *layer, QPainter &paint, int x0, - float minf, float maxf); + (LayerGeometryProvider *v, const ColourScaleLayer *layer, QPainter &paint, int x0, + double minf, double maxf); }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/LogNumericalScale.cpp --- a/layer/LogNumericalScale.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LogNumericalScale.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -27,43 +27,44 @@ //#define DEBUG_TIME_VALUE_LAYER 1 int -LogNumericalScale::getWidth(View *, +LogNumericalScale::getWidth(LayerGeometryProvider *, QPainter &paint) { return paint.fontMetrics().width("-000.00") + 10; } void -LogNumericalScale::paintVertical(View *v, +LogNumericalScale::paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, - float minlog, - float maxlog) + double minlog, + double maxlog) { int w = getWidth(v, paint) + x0; int n = 10; - float val = minlog; - float inc = (maxlog - val) / n; // even increments of log scale + double val = minlog; + double inc = (maxlog - val) / n; // even increments of log scale // smallest increment as displayed - float minDispInc = LogRange::unmap(minlog + inc) - LogRange::unmap(minlog); + double minDispInc = LogRange::unmap(minlog + inc) - LogRange::unmap(minlog); #ifdef DEBUG_TIME_VALUE_LAYER cerr << "min = " << minlog << ", max = " << maxlog << ", inc = " << inc << ", minDispInc = " << minDispInc << endl; #endif - char buffer[40]; + const int buflen = 40; + char buffer[buflen]; - float round = 1.f; + double round = 1.f; int dp = 0; if (minDispInc > 0) { - int prec = trunc(log10f(minDispInc)); + int prec = int(trunc(log10(minDispInc))); if (prec < 0) dp = -prec; - round = powf(10.f, prec); + round = pow(10.0, prec); if (dp > 4) dp = 4; #ifdef DEBUG_TIME_VALUE_LAYER cerr << "round = " << round << ", prec = " << prec << ", dp = " << dp << endl; @@ -78,11 +79,11 @@ bool drawText = true; if (i == n-1 && - v->height() < paint.fontMetrics().height() * (n*2)) { + v->getPaintHeight() < paint.fontMetrics().height() * (n*2)) { if (layer->getScaleUnits() != "") drawText = false; } - float dispval = LogRange::unmap(val); + double dispval = LogRange::unmap(val); dispval = floor(dispval / round) * round; #ifdef DEBUG_TIME_VALUE_LAYER @@ -98,13 +99,13 @@ continue; } - int digits = trunc(log10f(dispval)); + int digits = int(trunc(log10(dispval))); int sf = dp + (digits > 0 ? digits : 0); if (sf < 4) sf = 4; #ifdef DEBUG_TIME_VALUE_LAYER cerr << "sf = " << sf << endl; #endif - sprintf(buffer, "%.*g", sf, dispval); + snprintf(buffer, buflen, "%.*g", sf, dispval); QString label = QString(buffer); diff -r 282f4be8f058 -r c02c51ae5238 layer/LogNumericalScale.h --- a/layer/LogNumericalScale.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/LogNumericalScale.h Wed Apr 20 12:06:28 2016 +0100 @@ -19,17 +19,17 @@ #include class QPainter; -class View; +class LayerGeometryProvider; class VerticalScaleLayer; class LogNumericalScale { public: - int getWidth(View *v, QPainter &paint); + int getWidth(LayerGeometryProvider *v, QPainter &paint); void paintVertical - (View *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, - float minlog, float maxlog); + (LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, + double minlog, double maxlog); }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/NoteLayer.cpp --- a/layer/NoteLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/NoteLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -49,6 +49,10 @@ SingleColourLayer(), m_model(0), m_editing(false), + m_dragPointX(0), + m_dragPointY(0), + m_dragStartX(0), + m_dragStartY(0), m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")), m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")), m_editingCommand(0), @@ -56,12 +60,12 @@ m_scaleMinimum(0), m_scaleMaximum(0) { - + SVDEBUG << "constructed NoteLayer" << endl; } void NoteLayer::setModel(NoteModel *model) -{ +{ if (m_model == model) return; m_model = model; @@ -187,7 +191,7 @@ } bool -NoteLayer::isLayerScrollable(const View *v) const +NoteLayer::isLayerScrollable(const LayerGeometryProvider *v) const { QPoint discard; return !v->shouldIlluminateLocalFeatures(this, discard); @@ -205,7 +209,7 @@ } bool -NoteLayer::getValueExtents(float &min, float &max, +NoteLayer::getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const { if (!m_model) return false; @@ -214,8 +218,8 @@ if (shouldConvertMIDIToHz()) { unit = "Hz"; - min = Pitch::getFrequencyForPitch(lrintf(min)); - max = Pitch::getFrequencyForPitch(lrintf(max + 1)); + min = Pitch::getFrequencyForPitch(int(lrint(min))); + max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); } else unit = getScaleUnits(); if (m_verticalScale == MIDIRangeScale || @@ -225,7 +229,7 @@ } bool -NoteLayer::getDisplayExtents(float &min, float &max) const +NoteLayer::getDisplayExtents(double &min, double &max) const { if (!m_model || shouldAutoAlign()) return false; @@ -244,8 +248,8 @@ } if (shouldConvertMIDIToHz()) { - min = Pitch::getFrequencyForPitch(lrintf(min)); - max = Pitch::getFrequencyForPitch(lrintf(max + 1)); + min = Pitch::getFrequencyForPitch(int(lrint(min))); + max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); } #ifdef DEBUG_NOTE_LAYER @@ -256,7 +260,7 @@ } bool -NoteLayer::setDisplayExtents(float min, float max) +NoteLayer::setDisplayExtents(double min, double max) { if (!m_model) return false; @@ -298,7 +302,7 @@ RangeMapper *mapper = getNewVerticalZoomRangeMapper(); if (!mapper) return 0; - float dmin, dmax; + double dmin, dmax; getDisplayExtents(dmin, dmax); int nr = mapper->getPositionForValue(dmax - dmin); @@ -319,29 +323,29 @@ RangeMapper *mapper = getNewVerticalZoomRangeMapper(); if (!mapper) return; - float min, max; + double min, max; bool logarithmic; QString unit; getValueExtents(min, max, logarithmic, unit); - float dmin, dmax; + double dmin, dmax; getDisplayExtents(dmin, dmax); - float newdist = mapper->getValueForPosition(100 - step); + double newdist = mapper->getValueForPosition(100 - step); - float newmin, newmax; + double newmin, newmax; if (logarithmic) { // see SpectrogramLayer::setVerticalZoomStep - newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2; + newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2; newmin = newmax - newdist; // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl; } else { - float dmid = (dmax + dmin) / 2; + double dmid = (dmax + dmin) / 2; newmin = dmid - newdist / 2; newmax = dmid + newdist / 2; } @@ -368,7 +372,7 @@ RangeMapper *mapper; - float min, max; + double min, max; bool logarithmic; QString unit; getValueExtents(min, max, logarithmic, unit); @@ -385,11 +389,11 @@ } NoteModel::PointList -NoteLayer::getLocalPoints(View *v, int x) const +NoteLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { if (!m_model) return NoteModel::PointList(); - long frame = v->getFrameForX(x); + sv_frame_t frame = v->getFrameForX(x); NoteModel::PointList onPoints = m_model->getPoints(frame); @@ -407,11 +411,11 @@ if (prevPoints.empty()) { usePoints = nextPoints; - } else if (long(prevPoints.begin()->frame) < v->getStartFrame() && + } else if (int(prevPoints.begin()->frame) < v->getStartFrame() && !(nextPoints.begin()->frame > v->getEndFrame())) { usePoints = nextPoints; - } else if (long(nextPoints.begin()->frame) - frame < - frame - long(prevPoints.begin()->frame)) { + } else if (int(nextPoints.begin()->frame) - frame < + frame - int(prevPoints.begin()->frame)) { usePoints = nextPoints; } @@ -428,11 +432,11 @@ } bool -NoteLayer::getPointToDrag(View *v, int x, int y, NoteModel::Point &p) const +NoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, NoteModel::Point &p) const { if (!m_model) return false; - long frame = v->getFrameForX(x); + sv_frame_t frame = v->getFrameForX(x); NoteModel::PointList onPoints = m_model->getPoints(frame); if (onPoints.empty()) return false; @@ -456,7 +460,7 @@ } QString -NoteLayer::getFeatureDescription(View *v, QPoint &pos) const +NoteLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); @@ -502,9 +506,9 @@ if (shouldConvertMIDIToHz()) { - int mnote = lrintf(note.value); - int cents = lrintf((note.value - mnote) * 100); - float freq = Pitch::getFrequencyForPitch(mnote, cents); + int mnote = int(lrint(note.value)); + int cents = int(lrint((note.value - float(mnote)) * 100)); + double freq = Pitch::getFrequencyForPitch(mnote, cents); pitchText = tr("%1 (%2, %3 Hz)") .arg(Pitch::getPitchLabel(mnote, cents)) .arg(mnote) @@ -543,8 +547,8 @@ } bool -NoteLayer::snapToFeatureFrame(View *v, int &frame, - size_t &resolution, +NoteLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -563,7 +567,7 @@ } points = m_model->getPoints(frame, frame); - int snapped = frame; + sv_frame_t snapped = frame; bool found = false; for (NoteModel::PointList::const_iterator i = points.begin(); @@ -615,7 +619,7 @@ } void -NoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const +NoteLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const { min = 0.0; max = 0.0; @@ -633,8 +637,8 @@ max = m_model->getValueMaximum(); if (shouldConvertMIDIToHz()) { - min = Pitch::getFrequencyForPitch(lrintf(min)); - max = Pitch::getFrequencyForPitch(lrintf(max + 1)); + min = Pitch::getFrequencyForPitch(int(lrint(min))); + max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); } #ifdef DEBUG_NOTE_LAYER @@ -659,8 +663,8 @@ min = Pitch::getFrequencyForPitch(0); max = Pitch::getFrequencyForPitch(127); } else if (shouldConvertMIDIToHz()) { - min = Pitch::getFrequencyForPitch(lrintf(min)); - max = Pitch::getFrequencyForPitch(lrintf(max + 1)); + min = Pitch::getFrequencyForPitch(int(lrint(min))); + max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); } if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { @@ -673,11 +677,11 @@ } int -NoteLayer::getYForValue(View *v, float val) const +NoteLayer::getYForValue(LayerGeometryProvider *v, double val) const { - float min = 0.0, max = 0.0; + double min = 0.0, max = 0.0; bool logarithmic = false; - int h = v->height(); + int h = v->getPaintHeight(); getScaleExtents(v, min, max, logarithmic); @@ -686,8 +690,8 @@ #endif if (shouldConvertMIDIToHz()) { - val = Pitch::getFrequencyForPitch(lrintf(val), - lrintf((val - lrintf(val)) * 100)); + val = Pitch::getFrequencyForPitch(int(lrint(val)), + int(lrint((val - rint(val)) * 100))); #ifdef DEBUG_NOTE_LAYER cerr << "shouldConvertMIDIToHz true, val now = " << val << endl; #endif @@ -707,19 +711,19 @@ return y; } -float -NoteLayer::getValueForY(View *v, int y) const +double +NoteLayer::getValueForY(LayerGeometryProvider *v, int y) const { - float min = 0.0, max = 0.0; + double min = 0.0, max = 0.0; bool logarithmic = false; - int h = v->height(); + int h = v->getPaintHeight(); getScaleExtents(v, min, max, logarithmic); - float val = min + (float(h - y) * float(max - min)) / h; + double val = min + (double(h - y) * double(max - min)) / h; if (logarithmic) { - val = powf(10.f, val); + val = pow(10.0, val); } if (shouldConvertMIDIToHz()) { @@ -737,18 +741,18 @@ } void -NoteLayer::paint(View *v, QPainter &paint, QRect rect) const +NoteLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) return; - int sampleRate = m_model->getSampleRate(); + sv_samplerate_t sampleRate = m_model->getSampleRate(); if (!sampleRate) return; // Profiler profiler("NoteLayer::paint", true); int x0 = rect.left(), x1 = rect.right(); - long frame0 = v->getFrameForX(x0); - long frame1 = v->getFrameForX(x1); + sv_frame_t frame0 = v->getFrameForX(x0); + sv_frame_t frame1 = v->getFrameForX(x1); NoteModel::PointList points(m_model->getPoints(frame0, frame1)); if (points.empty()) return; @@ -761,8 +765,8 @@ // SVDEBUG << "NoteLayer::paint: resolution is " // << m_model->getResolution() << " frames" << endl; - float min = m_model->getValueMinimum(); - float max = m_model->getValueMaximum(); + double min = m_model->getValueMinimum(); + double max = m_model->getValueMaximum(); if (max == min) max = min + 1.0; QPoint localPos; @@ -826,7 +830,7 @@ } int -NoteLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const +NoteLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const { if (!m_model || shouldAutoAlign()) { return 0; @@ -840,16 +844,16 @@ } void -NoteLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const +NoteLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const { - if (!m_model) return; + if (!m_model || m_model->getPoints().empty()) return; QString unit; - float min, max; + double min, max; bool logarithmic; int w = getVerticalScaleWidth(v, false, paint); - int h = v->height(); + int h = v->getPaintHeight(); getScaleExtents(v, min, max, logarithmic); @@ -878,19 +882,19 @@ } void -NoteLayer::drawStart(View *v, QMouseEvent *e) +NoteLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; if (!m_model) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - float value = getValueForY(v, e->y()); + double value = getValueForY(v, e->y()); - m_editingPoint = NoteModel::Point(frame, value, 0, 0.8, tr("New Point")); + m_editingPoint = NoteModel::Point(frame, float(value), 0, 0.8f, tr("New Point")); m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); @@ -902,20 +906,20 @@ } void -NoteLayer::drawDrag(View *v, QMouseEvent *e) +NoteLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - float newValue = getValueForY(v, e->y()); + double newValue = getValueForY(v, e->y()); - long newFrame = m_editingPoint.frame; - long newDuration = frame - newFrame; + sv_frame_t newFrame = m_editingPoint.frame; + sv_frame_t newDuration = frame - newFrame; if (newDuration < 0) { newFrame = frame; newDuration = -newDuration; @@ -925,13 +929,13 @@ m_editingCommand->deletePoint(m_editingPoint); m_editingPoint.frame = newFrame; - m_editingPoint.value = newValue; + m_editingPoint.value = float(newValue); m_editingPoint.duration = newDuration; m_editingCommand->addPoint(m_editingPoint); } void -NoteLayer::drawEnd(View *, QMouseEvent *) +NoteLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *) { // SVDEBUG << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; @@ -941,7 +945,7 @@ } void -NoteLayer::eraseStart(View *v, QMouseEvent *e) +NoteLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return; @@ -956,12 +960,12 @@ } void -NoteLayer::eraseDrag(View *v, QMouseEvent *e) +NoteLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *) { } void -NoteLayer::eraseEnd(View *v, QMouseEvent *e) +NoteLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; @@ -981,7 +985,7 @@ } void -NoteLayer::editStart(View *v, QMouseEvent *e) +NoteLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; @@ -1004,7 +1008,7 @@ } void -NoteLayer::editDrag(View *v, QMouseEvent *e) +NoteLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl; @@ -1015,11 +1019,11 @@ int newx = m_dragPointX + xdist; int newy = m_dragPointY + ydist; - long frame = v->getFrameForX(newx); + sv_frame_t frame = v->getFrameForX(newx); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - float value = getValueForY(v, newy); + double value = getValueForY(v, newy); if (!m_editingCommand) { m_editingCommand = new NoteModel::EditCommand(m_model, @@ -1028,12 +1032,12 @@ m_editingCommand->deletePoint(m_editingPoint); m_editingPoint.frame = frame; - m_editingPoint.value = value; + m_editingPoint.value = float(value); m_editingCommand->addPoint(m_editingPoint); } void -NoteLayer::editEnd(View *, QMouseEvent *) +NoteLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) { // SVDEBUG << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; @@ -1061,7 +1065,7 @@ } bool -NoteLayer::editOpen(View *v, QMouseEvent *e) +NoteLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return false; @@ -1103,7 +1107,7 @@ } void -NoteLayer::moveSelection(Selection s, size_t newStartFrame) +NoteLayer::moveSelection(Selection s, sv_frame_t newStartFrame) { if (!m_model) return; @@ -1147,13 +1151,13 @@ if (s.contains(i->frame)) { - double targetStart = i->frame; - targetStart = newSize.getStartFrame() + - double(targetStart - s.getStartFrame()) * ratio; + double targetStart = double(i->frame); + targetStart = double(newSize.getStartFrame()) + + targetStart - double(s.getStartFrame()) * ratio; - double targetEnd = i->frame + i->duration; - targetEnd = newSize.getStartFrame() + - double(targetEnd - s.getStartFrame()) * ratio; + double targetEnd = double(i->frame + i->duration); + targetEnd = double(newSize.getStartFrame()) + + targetEnd - double(s.getStartFrame()) * ratio; NoteModel::Point newPoint(*i); newPoint.frame = lrint(targetStart); @@ -1189,7 +1193,7 @@ } void -NoteLayer::copy(View *v, Selection s, Clipboard &to) +NoteLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -1207,7 +1211,7 @@ } bool -NoteLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */) +NoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */) { if (!m_model) return false; @@ -1218,7 +1222,7 @@ if (clipboardHasDifferentAlignment(v, from)) { QMessageBox::StandardButton button = - QMessageBox::question(v, tr("Re-align pasted items?"), + QMessageBox::question(v->getView(), tr("Re-align pasted items?"), tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); @@ -1239,7 +1243,7 @@ i != points.end(); ++i) { if (!i->haveFrame()) continue; - size_t frame = 0; + sv_frame_t frame = 0; if (!realign) { @@ -1264,7 +1268,7 @@ if (i->haveLevel()) newPoint.level = i->getLevel(); if (i->haveDuration()) newPoint.duration = i->getDuration(); else { - size_t nextFrame = frame; + sv_frame_t nextFrame = frame; Clipboard::PointList::const_iterator j = i; for (; j != points.end(); ++j) { if (!j->haveFrame()) continue; @@ -1288,13 +1292,13 @@ } void -NoteLayer::addNoteOn(long frame, int pitch, int velocity) +NoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity) { - m_pendingNoteOns.insert(Note(frame, pitch, 0, float(velocity) / 127.0, "")); + m_pendingNoteOns.insert(Note(frame, float(pitch), 0, float(velocity) / 127.f, "")); } void -NoteLayer::addNoteOff(long frame, int pitch) +NoteLayer::addNoteOff(sv_frame_t frame, int pitch) { for (NoteSet::iterator i = m_pendingNoteOns.begin(); i != m_pendingNoteOns.end(); ++i) { diff -r 282f4be8f058 -r c02c51ae5238 layer/NoteLayer.h --- a/layer/NoteLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/NoteLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -35,37 +35,37 @@ public: NoteLayer(); - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const; - virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const; + virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const; + virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; - virtual bool snapToFeatureFrame(View *v, int &frame, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual void drawStart(View *v, QMouseEvent *); - virtual void drawDrag(View *v, QMouseEvent *); - virtual void drawEnd(View *v, QMouseEvent *); + virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void eraseStart(View *v, QMouseEvent *); - virtual void eraseDrag(View *v, QMouseEvent *); - virtual void eraseEnd(View *v, QMouseEvent *); + virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void editStart(View *v, QMouseEvent *); - virtual void editDrag(View *v, QMouseEvent *); - virtual void editEnd(View *v, QMouseEvent *); + virtual void editStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual bool editOpen(View *v, QMouseEvent *); + virtual bool editOpen(LayerGeometryProvider *v, QMouseEvent *); - virtual void moveSelection(Selection s, size_t newStartFrame); + virtual void moveSelection(Selection s, sv_frame_t newStartFrame); virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(View *v, Selection s, Clipboard &to); - virtual bool paste(View *v, const Clipboard &from, int frameOffset, + virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to); + virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive); virtual const Model *getModel() const { return m_model; } @@ -91,17 +91,17 @@ void setVerticalScale(VerticalScale scale); VerticalScale getVerticalScale() const { return m_verticalScale; } - virtual bool isLayerScrollable(const View *v) const; + virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; virtual bool isLayerEditable() const { return true; } - virtual int getCompletion(View *) const { return m_model->getCompletion(); } + virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); } - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &log, QString &unit) const; - virtual bool getDisplayExtents(float &min, float &max) const; - virtual bool setDisplayExtents(float min, float max); + virtual bool getDisplayExtents(double &min, double &max) const; + virtual bool setDisplayExtents(double min, double max); virtual int getVerticalZoomSteps(int &defaultStep) const; virtual int getCurrentVerticalZoomStep() const; @@ -113,13 +113,13 @@ * not be finally added to the layer until the corresponding * note-off. */ - void addNoteOn(long frame, int pitch, int velocity); + void addNoteOn(sv_frame_t frame, int pitch, int velocity); /** * Add a note-off. This will cause a note to appear, if and only * if there is a matching pending note-on. */ - void addNoteOff(long frame, int pitch); + void addNoteOff(sv_frame_t frame, int pitch); /** * Abandon all pending note-on events. @@ -132,19 +132,19 @@ void setProperties(const QXmlAttributes &attributes); /// VerticalScaleLayer methods - virtual int getYForValue(View *v, float value) const; - virtual float getValueForY(View *v, int y) const; + virtual int getYForValue(LayerGeometryProvider *v, double value) const; + virtual double getValueForY(LayerGeometryProvider *v, int y) const; virtual QString getScaleUnits() const; protected: - void getScaleExtents(View *, float &min, float &max, bool &log) const; + void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const; bool shouldConvertMIDIToHz() const; virtual int getDefaultColourHint(bool dark, bool &impose); - NoteModel::PointList getLocalPoints(View *v, int) const; + NoteModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const; - bool getPointToDrag(View *v, int x, int y, NoteModel::Point &) const; + bool getPointToDrag(LayerGeometryProvider *v, int x, int y, NoteModel::Point &) const; NoteModel *m_model; bool m_editing; @@ -160,8 +160,8 @@ typedef std::set NoteSet; NoteSet m_pendingNoteOns; - mutable float m_scaleMinimum; - mutable float m_scaleMaximum; + mutable double m_scaleMinimum; + mutable double m_scaleMaximum; bool shouldAutoAlign() const; diff -r 282f4be8f058 -r c02c51ae5238 layer/PaintAssistant.cpp --- a/layer/PaintAssistant.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/PaintAssistant.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -25,11 +25,11 @@ void PaintAssistant::paintVerticalLevelScale(QPainter &paint, QRect rect, - float minVal, float maxVal, + double minVal, double maxVal, Scale scale, int &mult, std::vector *vy) { - static float meterdbs[] = { -40, -30, -20, -15, -10, + static double meterdbs[] = { -40, -30, -20, -15, -10, -5, -3, -2, -1, -0.5, 0 }; int h = rect.height(), w = rect.width(); @@ -41,7 +41,7 @@ int n = 10; if (vy) vy->clear(); - float step = 0; + double step = 0; mult = 1; if (scale == LinearScale) { step = (maxVal - minVal) / n; @@ -53,8 +53,8 @@ if (round) { mult /= 10; // cerr << "\n\nstep goes from " << step; - step = float(round) / mult; - n = lrintf((maxVal - minVal) / step); + step = double(round) / mult; + n = int(lrint((maxVal - minVal) / step)); if (mult > 1) { mult /= 10; } @@ -64,7 +64,7 @@ for (int i = 0; i <= n; ++i) { - float val = 0.0, nval = 0.0; + double val = 0.0, nval = 0.0; QString text = ""; switch (scale) { @@ -166,20 +166,20 @@ } static int -dBscale(float sample, int m, float maxVal, float minVal) +dBscale(double sample, int m, double maxVal, double minVal) { if (sample < 0.0) return dBscale(-sample, m, maxVal, minVal); - float dB = AudioLevel::multiplier_to_dB(sample); - float mindB = AudioLevel::multiplier_to_dB(minVal); - float maxdB = AudioLevel::multiplier_to_dB(maxVal); + double dB = AudioLevel::multiplier_to_dB(sample); + double mindB = AudioLevel::multiplier_to_dB(minVal); + double maxdB = AudioLevel::multiplier_to_dB(maxVal); if (dB < mindB) return 0; if (dB > 0.0) return m; return int(((dB - mindB) * m) / (maxdB - mindB) + 0.1); } int -PaintAssistant::getYForValue(Scale scale, float value, - float minVal, float maxVal, +PaintAssistant::getYForValue(Scale scale, double value, + double minVal, double maxVal, int minY, int height) { int vy = 0; diff -r 282f4be8f058 -r c02c51ae5238 layer/PaintAssistant.h --- a/layer/PaintAssistant.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/PaintAssistant.h Wed Apr 20 12:06:28 2016 +0100 @@ -27,12 +27,12 @@ enum Scale { LinearScale, MeterScale, dBScale }; static void paintVerticalLevelScale(QPainter &p, QRect rect, - float minVal, float maxVal, + double minVal, double maxVal, Scale scale, int &multRtn, std::vector *markCoordRtns = 0); - static int getYForValue(Scale scale, float value, - float minVal, float maxVal, + static int getYForValue(Scale scale, double value, + double minVal, double maxVal, int minY, int height); }; diff -r 282f4be8f058 -r c02c51ae5238 layer/PianoScale.cpp --- a/layer/PianoScale.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/PianoScale.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -21,14 +21,14 @@ #include "base/Pitch.h" -#include "view/View.h" +#include "view/LayerGeometryProvider.h" void -PianoScale::paintPianoVertical(View *v, +PianoScale::paintPianoVertical(LayerGeometryProvider *v, QPainter &paint, QRect r, - float minf, - float maxf) + double minf, + double maxf) { int x0 = r.x(), y0 = r.y(), x1 = r.x() + r.width(), y1 = r.y() + r.height(); @@ -39,8 +39,8 @@ for (int i = 0; i < 128; ++i) { - float f = Pitch::getFrequencyForPitch(i); - int y = lrintf(v->getYForFrequency(f, minf, maxf, true)); + double f = Pitch::getFrequencyForPitch(i); + int y = int(lrint(v->getYForFrequency(f, minf, maxf, true))); if (y < y0 - 2) break; if (y > y1 + 2) { diff -r 282f4be8f058 -r c02c51ae5238 layer/PianoScale.h --- a/layer/PianoScale.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/PianoScale.h Wed Apr 20 12:06:28 2016 +0100 @@ -19,13 +19,13 @@ #include class QPainter; -class View; +class LayerGeometryProvider; class PianoScale { public: void paintPianoVertical - (View *v, QPainter &paint, QRect rect, float minf, float maxf); + (LayerGeometryProvider *v, QPainter &paint, QRect rect, double minf, double maxf); }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/RegionLayer.cpp --- a/layer/RegionLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/RegionLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -47,6 +47,10 @@ SingleColourLayer(), m_model(0), m_editing(false), + m_dragPointX(0), + m_dragPointY(0), + m_dragStartX(0), + m_dragStartY(0), m_originalPoint(0, 0.0, 0, tr("New Region")), m_editingPoint(0, 0.0, 0, tr("New Region")), m_editingCommand(0), @@ -240,7 +244,7 @@ } bool -RegionLayer::isLayerScrollable(const View *v) const +RegionLayer::isLayerScrollable(const LayerGeometryProvider *v) const { QPoint discard; return !v->shouldIlluminateLocalFeatures(this, discard); @@ -271,7 +275,7 @@ } bool -RegionLayer::getValueExtents(float &min, float &max, +RegionLayer::getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const { if (!m_model) return false; @@ -285,7 +289,7 @@ } bool -RegionLayer::getDisplayExtents(float &min, float &max) const +RegionLayer::getDisplayExtents(double &min, double &max) const { if (!m_model || m_verticalScale == AutoAlignScale || @@ -298,11 +302,11 @@ } RegionModel::PointList -RegionLayer::getLocalPoints(View *v, int x) const +RegionLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { if (!m_model) return RegionModel::PointList(); - long frame = v->getFrameForX(x); + sv_frame_t frame = v->getFrameForX(x); RegionModel::PointList onPoints = m_model->getPoints(frame); @@ -341,11 +345,11 @@ } bool -RegionLayer::getPointToDrag(View *v, int x, int y, RegionModel::Point &p) const +RegionLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, RegionModel::Point &p) const { if (!m_model) return false; - long frame = v->getFrameForX(x); + sv_frame_t frame = v->getFrameForX(x); RegionModel::PointList onPoints = m_model->getPoints(frame); if (onPoints.empty()) return false; @@ -367,7 +371,7 @@ } QString -RegionLayer::getLabelPreceding(size_t frame) const +RegionLayer::getLabelPreceding(sv_frame_t frame) const { if (!m_model) return ""; RegionModel::PointList points = m_model->getPreviousPoints(frame); @@ -379,7 +383,7 @@ } QString -RegionLayer::getFeatureDescription(View *v, QPoint &pos) const +RegionLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); @@ -449,8 +453,8 @@ } bool -RegionLayer::snapToFeatureFrame(View *v, int &frame, - size_t &resolution, +RegionLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -469,7 +473,7 @@ } points = m_model->getPoints(frame, frame); - int snapped = frame; + sv_frame_t snapped = frame; bool found = false; for (RegionModel::PointList::const_iterator i = points.begin(); @@ -532,8 +536,8 @@ } bool -RegionLayer::snapToSimilarFeature(View *v, int &frame, - size_t &resolution, +RegionLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -547,8 +551,8 @@ RegionModel::PointList::const_iterator i; - int matchframe = frame; - float matchvalue = 0.f; + sv_frame_t matchframe = frame; + double matchvalue = 0.f; for (i = close.begin(); i != close.end(); ++i) { if (i->frame > frame) break; @@ -556,27 +560,35 @@ matchframe = i->frame; } - int snapped = frame; + sv_frame_t snapped = frame; bool found = false; bool distant = false; - float epsilon = 0.0001; + double epsilon = 0.0001; i = close.begin(); // Scan through the close points first, then the more distant ones - // if no suitable close one is found + // if no suitable close one is found. So the while-termination + // condition here can only happen once i has passed through the + // whole of the close container and then the whole of the separate + // points container. The two iterators are totally distinct, but + // have the same type so we cheekily use the same variable and a + // single loop for both. while (i != points.end()) { - if (i == close.end()) { - i = points.begin(); - distant = true; + if (!distant) { + if (i == close.end()) { + // switch from the close container to the points container + i = points.begin(); + distant = true; + } } if (snap == SnapRight) { if (i->frame > matchframe && - fabsf(i->value - matchvalue) < epsilon) { + fabs(i->value - matchvalue) < epsilon) { snapped = i->frame; found = true; break; @@ -585,7 +597,7 @@ } else if (snap == SnapLeft) { if (i->frame < matchframe) { - if (fabsf(i->value - matchvalue) < epsilon) { + if (fabs(i->value - matchvalue) < epsilon) { snapped = i->frame; found = true; // don't break, as the next may be better } @@ -612,7 +624,7 @@ } void -RegionLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const +RegionLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const { min = 0.0; max = 0.0; @@ -664,33 +676,33 @@ } int -RegionLayer::spacingIndexToY(View *v, int i) const +RegionLayer::spacingIndexToY(LayerGeometryProvider *v, int i) const { - int h = v->height(); - int n = m_spacingMap.size(); + int h = v->getPaintHeight(); + int n = int(m_spacingMap.size()); // this maps from i (spacing of the value from the spacing // map) and n (number of region types) to y int y = h - (((h * i) / n) + (h / (2 * n))); return y; } -float -RegionLayer::yToSpacingIndex(View *v, int y) const +double +RegionLayer::yToSpacingIndex(LayerGeometryProvider *v, int y) const { - // we return an inexact result here (float rather than int) - int h = v->height(); - int n = m_spacingMap.size(); + // we return an inexact result here (double rather than int) + int h = v->getPaintHeight(); + int n = int(m_spacingMap.size()); // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i) - float vh = float(2*h*n - h - 2*n*y) / float(2*h); + double vh = double(2*h*n - h - 2*n*y) / double(2*h); return vh; } int -RegionLayer::getYForValue(View *v, float val) const +RegionLayer::getYForValue(LayerGeometryProvider *v, double val) const { - float min = 0.0, max = 0.0; + double min = 0.0, max = 0.0; bool logarithmic = false; - int h = v->height(); + int h = v->getPaintHeight(); if (m_verticalScale == EqualSpaced) { @@ -720,18 +732,18 @@ } } -float -RegionLayer::getValueForY(View *v, int y) const +double +RegionLayer::getValueForY(LayerGeometryProvider *v, int y) const { return getValueForY(v, y, -1); } -float -RegionLayer::getValueForY(View *v, int y, int avoid) const +double +RegionLayer::getValueForY(LayerGeometryProvider *v, int y, int avoid) const { - float min = 0.0, max = 0.0; + double min = 0.0, max = 0.0; bool logarithmic = false; - int h = v->height(); + int h = v->getPaintHeight(); if (m_verticalScale == EqualSpaced) { @@ -745,14 +757,14 @@ // one of the m/n divisions in the y scale, we should snap to // the value of the mth region. - float vh = yToSpacingIndex(v, y); + double vh = yToSpacingIndex(v, y); // spacings in the map are integral, so find the closest one, // map it back to its y coordinate, and see how far we are // from it - int n = m_spacingMap.size(); - int ivh = lrintf(vh); + int n = int(m_spacingMap.size()); + int ivh = int(lrint(vh)); if (ivh < 0) ivh = 0; if (ivh > n-1) ivh = n-1; int iy = spacingIndexToY(v, ivh); @@ -771,7 +783,7 @@ // cerr << "nearest existing value = " << i->first << " at " << iy << endl; - float val = 0; + double val = 0; // cerr << "note: avoid = " << avoid << ", i->second = " << i->second << endl; @@ -813,10 +825,10 @@ getScaleExtents(v, min, max, logarithmic); - float val = min + (float(h - y) * float(max - min)) / h; + double val = min + (double(h - y) * double(max - min)) / h; if (logarithmic) { - val = powf(10.f, val); + val = pow(10.0, val); } return val; @@ -824,9 +836,9 @@ } QColor -RegionLayer::getColourForValue(View *v, float val) const +RegionLayer::getColourForValue(LayerGeometryProvider *v, double val) const { - float min, max; + double min, max; bool log; getScaleExtents(v, min, max, log); @@ -854,18 +866,18 @@ } void -RegionLayer::paint(View *v, QPainter &paint, QRect rect) const +RegionLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) return; - int sampleRate = m_model->getSampleRate(); + sv_samplerate_t sampleRate = m_model->getSampleRate(); if (!sampleRate) return; // Profiler profiler("RegionLayer::paint", true); int x0 = rect.left() - 40, x1 = rect.right(); - long frame0 = v->getFrameForX(x0); - long frame1 = v->getFrameForX(x1); + sv_frame_t frame0 = v->getFrameForX(x0); + sv_frame_t frame1 = v->getFrameForX(x1); RegionModel::PointList points(m_model->getPoints(frame0, frame1)); if (points.empty()) return; @@ -878,8 +890,8 @@ // SVDEBUG << "RegionLayer::paint: resolution is " // << m_model->getResolution() << " frames" << endl; - float min = m_model->getValueMinimum(); - float max = m_model->getValueMaximum(); + double min = m_model->getValueMinimum(); + double max = m_model->getValueMaximum(); if (max == min) max = min + 1.0; QPoint localPos; @@ -901,7 +913,6 @@ //!!! coord is never completely flat on the top or bottom int fontHeight = paint.fontMetrics().height(); - int fontAscent = paint.fontMetrics().ascent(); for (RegionModel::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { @@ -931,7 +942,7 @@ if (w < 1) w = 1; if (m_plotStyle == PlotSegmentation) { - paint.setPen(getForegroundQColor(v)); + paint.setPen(getForegroundQColor(v->getView())); paint.setBrush(getColourForValue(v, p.value)); } else { paint.setPen(getBaseQColor()); @@ -947,15 +958,15 @@ RegionModel::Point::Comparator()(illuminatePoint, p) || RegionModel::Point::Comparator()(p, illuminatePoint)) { - paint.setPen(QPen(getForegroundQColor(v), 1)); - paint.drawLine(x, 0, x, v->height()); + paint.setPen(QPen(getForegroundQColor(v->getView()), 1)); + paint.drawLine(x, 0, x, v->getPaintHeight()); paint.setPen(Qt::NoPen); } else { - paint.setPen(QPen(getForegroundQColor(v), 2)); + paint.setPen(QPen(getForegroundQColor(v->getView()), 2)); } - paint.drawRect(x, -1, ex - x, v->height() + 2); + paint.drawRect(x, -1, ex - x, v->getPaintHeight() + 2); } else { @@ -1029,7 +1040,7 @@ labelX = x + 5; labelY = v->getTextLabelHeight(this, paint); if (labelX < nextLabelMinX) { - if (lastLabelY < v->height()/2) { + if (lastLabelY < v->getPaintHeight()/2) { labelY = lastLabelY + fontHeight; } } @@ -1045,7 +1056,7 @@ } int -RegionLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const +RegionLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const { if (!m_model || m_verticalScale == AutoAlignScale || @@ -1067,16 +1078,15 @@ } void -RegionLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const +RegionLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const { - if (!m_model) return; + if (!m_model || m_model->getPoints().empty()) return; QString unit; - float min, max; + double min, max; bool logarithmic; int w = getVerticalScaleWidth(v, false, paint); - int h = v->height(); if (m_plotStyle == PlotSegmentation) { @@ -1111,17 +1121,17 @@ } void -RegionLayer::drawStart(View *v, QMouseEvent *e) +RegionLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - float value = getValueForY(v, e->y()); + double value = getValueForY(v, e->y()); - m_editingPoint = RegionModel::Point(frame, value, 0, ""); + m_editingPoint = RegionModel::Point(frame, float(value), 0, ""); m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); @@ -1135,19 +1145,19 @@ } void -RegionLayer::drawDrag(View *v, QMouseEvent *e) +RegionLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - float newValue = m_editingPoint.value; + double newValue = m_editingPoint.value; if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y()); - long newFrame = m_editingPoint.frame; - long newDuration = frame - newFrame; + sv_frame_t newFrame = m_editingPoint.frame; + sv_frame_t newDuration = frame - newFrame; if (newDuration < 0) { newFrame = frame; newDuration = -newDuration; @@ -1157,7 +1167,7 @@ m_editingCommand->deletePoint(m_editingPoint); m_editingPoint.frame = newFrame; - m_editingPoint.value = newValue; + m_editingPoint.value = float(newValue); m_editingPoint.duration = newDuration; m_editingCommand->addPoint(m_editingPoint); @@ -1165,7 +1175,7 @@ } void -RegionLayer::drawEnd(View *, QMouseEvent *) +RegionLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *) { if (!m_model || !m_editing) return; finish(m_editingCommand); @@ -1176,7 +1186,7 @@ } void -RegionLayer::eraseStart(View *v, QMouseEvent *e) +RegionLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return; @@ -1192,12 +1202,12 @@ } void -RegionLayer::eraseDrag(View *v, QMouseEvent *e) +RegionLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *) { } void -RegionLayer::eraseEnd(View *v, QMouseEvent *e) +RegionLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; @@ -1219,7 +1229,7 @@ } void -RegionLayer::editStart(View *v, QMouseEvent *e) +RegionLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return; @@ -1244,7 +1254,7 @@ } void -RegionLayer::editDrag(View *v, QMouseEvent *e) +RegionLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; @@ -1253,7 +1263,7 @@ int newx = m_dragPointX + xdist; int newy = m_dragPointY + ydist; - long frame = v->getFrameForX(newx); + sv_frame_t frame = v->getFrameForX(newx); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); @@ -1264,7 +1274,7 @@ // ... unless there are other points with the same value if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1; - float value = getValueForY(v, newy, avoid); + double value = getValueForY(v, newy, avoid); if (!m_editingCommand) { m_editingCommand = new RegionModel::EditCommand(m_model, @@ -1273,13 +1283,13 @@ m_editingCommand->deletePoint(m_editingPoint); m_editingPoint.frame = frame; - m_editingPoint.value = value; + m_editingPoint.value = float(value); m_editingCommand->addPoint(m_editingPoint); recalcSpacing(); } void -RegionLayer::editEnd(View *, QMouseEvent *e) +RegionLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) { if (!m_model || !m_editing) return; @@ -1307,7 +1317,7 @@ } bool -RegionLayer::editOpen(View *v, QMouseEvent *e) +RegionLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return false; @@ -1348,7 +1358,7 @@ } void -RegionLayer::moveSelection(Selection s, size_t newStartFrame) +RegionLayer::moveSelection(Selection s, sv_frame_t newStartFrame) { if (!m_model) return; @@ -1393,13 +1403,13 @@ if (s.contains(i->frame)) { - double targetStart = i->frame; - targetStart = newSize.getStartFrame() + - double(targetStart - s.getStartFrame()) * ratio; + double targetStart = double(i->frame); + targetStart = double(newSize.getStartFrame()) + + targetStart - double(s.getStartFrame()) * ratio; - double targetEnd = i->frame + i->duration; - targetEnd = newSize.getStartFrame() + - double(targetEnd - s.getStartFrame()) * ratio; + double targetEnd = double(i->frame + i->duration); + targetEnd = double(newSize.getStartFrame()) + + targetEnd - double(s.getStartFrame()) * ratio; RegionModel::Point newPoint(*i); newPoint.frame = lrint(targetStart); @@ -1437,7 +1447,7 @@ } void -RegionLayer::copy(View *v, Selection s, Clipboard &to) +RegionLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -1455,7 +1465,7 @@ } bool -RegionLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */) +RegionLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */) { if (!m_model) return false; @@ -1466,7 +1476,7 @@ if (clipboardHasDifferentAlignment(v, from)) { QMessageBox::StandardButton button = - QMessageBox::question(v, tr("Re-align pasted items?"), + QMessageBox::question(v->getView(), tr("Re-align pasted items?"), tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); @@ -1487,7 +1497,7 @@ i != points.end(); ++i) { if (!i->haveFrame()) continue; - size_t frame = 0; + sv_frame_t frame = 0; if (!realign) { @@ -1511,7 +1521,7 @@ m_model->getValueMaximum()) / 2; if (i->haveDuration()) newPoint.duration = i->getDuration(); else { - size_t nextFrame = frame; + sv_frame_t nextFrame = frame; Clipboard::PointList::const_iterator j = i; for (; j != points.end(); ++j) { if (!j->haveFrame()) continue; diff -r 282f4be8f058 -r c02c51ae5238 layer/RegionLayer.h --- a/layer/RegionLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/RegionLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -39,41 +39,41 @@ public: RegionLayer(); - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const; - virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const; + virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const; + virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; - virtual QString getLabelPreceding(size_t) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; + virtual QString getLabelPreceding(sv_frame_t) const; - virtual bool snapToFeatureFrame(View *v, int &frame, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual bool snapToSimilarFeature(View *v, int &frame, - size_t &resolution, + virtual bool snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual void drawStart(View *v, QMouseEvent *); - virtual void drawDrag(View *v, QMouseEvent *); - virtual void drawEnd(View *v, QMouseEvent *); + virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void eraseStart(View *v, QMouseEvent *); - virtual void eraseDrag(View *v, QMouseEvent *); - virtual void eraseEnd(View *v, QMouseEvent *); + virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void editStart(View *v, QMouseEvent *); - virtual void editDrag(View *v, QMouseEvent *); - virtual void editEnd(View *v, QMouseEvent *); + virtual void editStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual bool editOpen(View *v, QMouseEvent *); + virtual bool editOpen(LayerGeometryProvider *v, QMouseEvent *); - virtual void moveSelection(Selection s, size_t newStartFrame); + virtual void moveSelection(Selection s, sv_frame_t newStartFrame); virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(View *v, Selection s, Clipboard &to); - virtual bool paste(View *v, const Clipboard &from, int frameOffset, + virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to); + virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive); virtual const Model *getModel() const { return m_model; } @@ -110,16 +110,16 @@ void setPlotStyle(PlotStyle style); PlotStyle getPlotStyle() const { return m_plotStyle; } - virtual bool isLayerScrollable(const View *v) const; + virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; virtual bool isLayerEditable() const { return true; } - virtual int getCompletion(View *) const { return m_model->getCompletion(); } + virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); } - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &log, QString &unit) const; - virtual bool getDisplayExtents(float &min, float &max) const; + virtual bool getDisplayExtents(double &min, double &max) const; virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; @@ -127,23 +127,23 @@ void setProperties(const QXmlAttributes &attributes); /// VerticalScaleLayer and ColourScaleLayer methods - int getYForValue(View *v, float value) const; - float getValueForY(View *v, int y) const; + int getYForValue(LayerGeometryProvider *v, double value) const; + double getValueForY(LayerGeometryProvider *v, int y) const; virtual QString getScaleUnits() const; - QColor getColourForValue(View *v, float value) const; + QColor getColourForValue(LayerGeometryProvider *v, double value) const; protected slots: void recalcSpacing(); protected: - float getValueForY(View *v, int y, int avoid) const; - void getScaleExtents(View *, float &min, float &max, bool &log) const; + double getValueForY(LayerGeometryProvider *v, int y, int avoid) const; + void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const; virtual int getDefaultColourHint(bool dark, bool &impose); - RegionModel::PointList getLocalPoints(View *v, int x) const; + RegionModel::PointList getLocalPoints(LayerGeometryProvider *v, int x) const; - bool getPointToDrag(View *v, int x, int y, RegionModel::Point &) const; + bool getPointToDrag(LayerGeometryProvider *v, int x, int y, RegionModel::Point &) const; RegionModel *m_model; bool m_editing; @@ -158,7 +158,7 @@ int m_colourMap; PlotStyle m_plotStyle; - typedef std::map SpacingMap; + typedef std::map SpacingMap; // region value -> ordering SpacingMap m_spacingMap; @@ -166,8 +166,8 @@ // region value -> number of regions with this value SpacingMap m_distributionMap; - int spacingIndexToY(View *v, int i) const; - float yToSpacingIndex(View *v, int y) const; + int spacingIndexToY(LayerGeometryProvider *v, int i) const; + double yToSpacingIndex(LayerGeometryProvider *v, int y) const; void finish(RegionModel::EditCommand *command) { Command *c = command->finish(); diff -r 282f4be8f058 -r c02c51ae5238 layer/ScrollableImageCache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/ScrollableImageCache.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,206 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "ScrollableImageCache.h" + +#include +using namespace std; + +//#define DEBUG_SCROLLABLE_IMAGE_CACHE 1 + +void +ScrollableImageCache::scrollTo(sv_frame_t newStartFrame) +{ + if (!m_v) throw std::logic_error("ScrollableImageCache: not associated with a LayerGeometryProvider"); + + int dx = (m_v->getXForFrame(m_startFrame) - + m_v->getXForFrame(newStartFrame)); + +#ifdef DEBUG_SCROLLABLE_IMAGE_CACHE + cerr << "ScrollableImageCache::scrollTo: start frame " << m_startFrame + << " -> " << newStartFrame << ", dx = " << dx << endl; +#endif + + m_startFrame = newStartFrame; + + if (!isValid()) { + return; + } + + int w = m_image.width(); + + if (dx == 0) { + // haven't moved + return; + } + + if (dx <= -w || dx >= w) { + // scrolled entirely off + invalidate(); + return; + } + + // dx is in range, cache is scrollable + + int dxp = dx; + if (dxp < 0) dxp = -dxp; + + int copylen = (w - dxp) * int(sizeof(QRgb)); + for (int y = 0; y < m_image.height(); ++y) { + QRgb *line = (QRgb *)m_image.scanLine(y); + if (dx < 0) { + memmove(line, line + dxp, copylen); + } else { + memmove(line + dxp, line, copylen); + } + } + + // update valid area + + int px = m_left; + int pw = m_width; + + px += dx; + + if (dx < 0) { + // we scrolled left + if (px < 0) { + pw += px; + px = 0; + if (pw < 0) { + pw = 0; + } + } + } else { + // we scrolled right + if (px + pw > w) { + pw = w - px; + if (pw < 0) { + pw = 0; + } + } + } + + m_left = px; + m_width = pw; +} + +void +ScrollableImageCache::adjustToTouchValidArea(int &left, int &width, + bool &isLeftOfValidArea) const +{ +#ifdef DEBUG_SCROLLABLE_IMAGE_CACHE + cerr << "ScrollableImageCache::adjustToTouchValidArea: left " << left + << ", width " << width << endl; + cerr << "ScrollableImageCache: my left " << m_left + << ", width " << m_width << " so right " << (m_left + m_width) << endl; +#endif + if (left < m_left) { + isLeftOfValidArea = true; + if (left + width <= m_left + m_width) { + width = m_left - left; + } +#ifdef DEBUG_SCROLLABLE_IMAGE_CACHE + cerr << "ScrollableImageCache: we're left of valid area, adjusted width to " << width << endl; +#endif + } else { + isLeftOfValidArea = false; + width = left + width - (m_left + m_width); + left = m_left + m_width; + if (width < 0) width = 0; +#ifdef DEBUG_SCROLLABLE_IMAGE_CACHE + cerr << "ScrollableImageCache: we're right of valid area, adjusted left to " << left << ", width to " << width << endl; +#endif + } +} + +void +ScrollableImageCache::drawImage(int left, + int width, + QImage image, + int imageLeft, + int imageWidth) +{ + if (image.height() != m_image.height()) { + cerr << "ScrollableImageCache::drawImage: ERROR: Supplied image height " + << image.height() << " does not match cache height " + << m_image.height() << endl; + throw std::logic_error("Image height must match cache height in ScrollableImageCache::drawImage"); + } + if (left < 0 || width < 0 || left + width > m_image.width()) { + cerr << "ScrollableImageCache::drawImage: ERROR: Target area (left = " + << left << ", width = " << width << ", so right = " << left + width + << ") out of bounds for cache of width " << m_image.width() << endl; + throw std::logic_error("Target area out of bounds in ScrollableImageCache::drawImage"); + } + if (imageLeft < 0 || imageWidth < 0 || + imageLeft + imageWidth > image.width()) { + cerr << "ScrollableImageCache::drawImage: ERROR: Source area (left = " + << imageLeft << ", width = " << imageWidth << ", so right = " + << imageLeft + imageWidth << ") out of bounds for image of " + << "width " << image.width() << endl; + throw std::logic_error("Source area out of bounds in ScrollableImageCache::drawImage"); + } + + QPainter painter(&m_image); + painter.drawImage(QRect(left, 0, width, m_image.height()), + image, + QRect(imageLeft, 0, imageWidth, image.height())); + painter.end(); + + if (!isValid()) { + m_left = left; + m_width = width; + return; + } + + if (left < m_left) { + if (left + width > m_left + m_width) { + // new image completely contains the old valid area -- + // use the new area as is + m_left = left; + m_width = width; + } else if (left + width < m_left) { + // new image completely off left of old valid area -- + // we can't extend the valid area because the bit in + // between is not valid, so must use the new area only + m_left = left; + m_width = width; + } else { + // new image overlaps old valid area on left side -- + // use new left edge, and extend width to existing + // right edge + m_width = (m_left + m_width) - left; + m_left = left; + } + } else { + if (left > m_left + m_width) { + // new image completely off right of old valid area -- + // we can't extend the valid area because the bit in + // between is not valid, so must use the new area only + m_left = left; + m_width = width; + } else if (left + width > m_left + m_width) { + // new image overlaps old valid area on right side -- + // use existing left edge, and extend width to new + // right edge + m_width = (left + width) - m_left; + // (m_left unchanged) + } else { + // new image completely contained within old valid + // area -- leave the old area unchanged + } + } +} + diff -r 282f4be8f058 -r c02c51ae5238 layer/ScrollableImageCache.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/ScrollableImageCache.h Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,145 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SCROLLABLE_IMAGE_CACHE_H +#define SCROLLABLE_IMAGE_CACHE_H + +#include "base/BaseTypes.h" + +#include "view/LayerGeometryProvider.h" + +#include +#include +#include + +/** + * A cached image for a view that scrolls horizontally, primarily the + * spectrogram. The cache object holds an image, reports the size of + * the image (likely the same as the underlying view, but it's the + * caller's responsibility to set the size appropriately), can scroll + * the image, and can report and update which contiguous horizontal + * range of the image is valid. + * + * The only way to *update* the valid area in a cache is to draw to it + * using the drawImage call. + */ +class ScrollableImageCache +{ +public: + ScrollableImageCache(const LayerGeometryProvider *v = 0) : + m_v(v), + m_left(0), + m_width(0), + m_startFrame(0), + m_zoomLevel(0) + {} + + void invalidate() { + m_width = 0; + } + + bool isValid() const { + return m_width > 0; + } + + QSize getSize() const { + return m_image.size(); + } + + void resize(QSize newSize) { + m_image = QImage(newSize, QImage::Format_ARGB32_Premultiplied); + invalidate(); + } + + int getValidLeft() const { + return m_left; + } + + int getValidWidth() const { + return m_width; + } + + int getValidRight() const { + return m_left + m_width; + } + + QRect getValidArea() const { + return QRect(m_left, 0, m_width, m_image.height()); + } + + int getZoomLevel() const { + return m_zoomLevel; + } + + void setZoomLevel(int zoom) { + m_zoomLevel = zoom; + invalidate(); + } + + sv_frame_t getStartFrame() const { + return m_startFrame; + } + + /** + * Set the start frame and invalidate the cache. To scroll, + * i.e. to set the start frame while retaining cache validity + * where possible, use scrollTo() instead. + */ + void setStartFrame(sv_frame_t frame) { + m_startFrame = frame; + invalidate(); + } + + const QImage &getImage() const { + return m_image; + } + + /** + * Set the new start frame for the cache, if possible also moving + * along any existing valid data within the cache so that it + * continues to be valid for the new start frame. + */ + void scrollTo(sv_frame_t newStartFrame); + + /** + * Take a left coordinate and width describing a region, and + * adjust them so that they are contiguous with the cache valid + * region and so that the union of the adjusted region with the + * cache valid region contains the supplied region. + */ + void adjustToTouchValidArea(int &left, int &width, + bool &isLeftOfValidArea) const; + /** + * Draw from an image onto the cache. The supplied image must have + * the same height as the cache and the full height is always + * drawn. The left and width parameters determine the target + * region of the cache, the imageLeft and imageWidth parameters + * the source region of the image. + */ + void drawImage(int left, + int width, + QImage image, + int imageLeft, + int imageWidth); + +private: + const LayerGeometryProvider *m_v; + QImage m_image; + int m_left; // of valid region + int m_width; // of valid region + sv_frame_t m_startFrame; + int m_zoomLevel; +}; + +#endif diff -r 282f4be8f058 -r c02c51ae5238 layer/ShowLayerCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/ShowLayerCommand.h Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,42 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _SHOW_LAYER_COMMAND_H_ +#define _SHOW_LAYER_COMMAND_H_ + +#include "base/Command.h" + +class ShowLayerCommand : public Command +{ +public: + ShowLayerCommand(View *view, Layer *layer, bool show, QString commandName) : + m_view(view), m_layer(layer), m_show(show), m_name(commandName) { } + void execute() { + m_layer->showLayer(m_view, m_show); + } + void unexecute() { + m_layer->showLayer(m_view, !m_show); + } + QString getName() const { + return m_name; + } +protected: + View *m_view; + Layer *m_layer; + bool m_show; + QString m_name; +}; + +#endif diff -r 282f4be8f058 -r c02c51ae5238 layer/SingleColourLayer.cpp --- a/layer/SingleColourLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/SingleColourLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -32,9 +32,17 @@ m_colourExplicitlySet(false), m_defaultColourSet(false) { + // Reference current colour because setDefaulColourFor + // will unreference it before (possibly) changing it. + refColor(); setDefaultColourFor(0); } +SingleColourLayer::~SingleColourLayer() +{ + unrefColor(); +} + QPixmap SingleColourLayer::getLayerPresentationPixmap(QSize size) const { @@ -107,7 +115,7 @@ { if (name == "Colour") { ColourDatabase *db = ColourDatabase::getInstance(); - if (value >= 0 && size_t(value) < db->getColourCount()) { + if (value >= 0 && value < db->getColourCount()) { return db->getColourName(value); } } @@ -129,7 +137,7 @@ } void -SingleColourLayer::setDefaultColourFor(View *v) +SingleColourLayer::setDefaultColourFor(LayerGeometryProvider *v) { #ifdef DEBUG_COLOUR_SELECTION SVDEBUG << "SingleColourLayer::setDefaultColourFor: m_colourExplicitlySet = " << m_colourExplicitlySet << ", m_defaultColourSet " << m_defaultColourSet << endl; @@ -152,10 +160,6 @@ int hint = -1; bool impose = false; if (v) { - if (m_colourRefCount.find(m_colour) != m_colourRefCount.end() && - m_colourRefCount[m_colour] > 0) { - m_colourRefCount[m_colour]--; - } // We don't want to call this if !v because that probably // means we're being called from the constructor, and this is // a virtual function @@ -174,6 +178,8 @@ return; } + unrefColor(); + int bestCount = 0, bestColour = -1; for (int i = 0; i < cdb->getColourCount(); ++i) { @@ -203,15 +209,11 @@ cerr << endl; #endif } - + if (bestColour < 0) m_colour = 0; else m_colour = bestColour; - if (m_colourRefCount.find(m_colour) == m_colourRefCount.end()) { - m_colourRefCount[m_colour] = 1; - } else { - m_colourRefCount[m_colour]++; - } + refColor(); } void @@ -221,18 +223,9 @@ if (m_colour == colour) return; - if (m_colourRefCount.find(m_colour) != m_colourRefCount.end() && - m_colourRefCount[m_colour] > 0) { - m_colourRefCount[m_colour]--; - } - + refColor(); m_colour = colour; - - if (m_colourRefCount.find(m_colour) == m_colourRefCount.end()) { - m_colourRefCount[m_colour] = 1; - } else { - m_colourRefCount[m_colour]++; - } + unrefColor(); flagBaseColourChanged(); emit layerParametersChanged(); @@ -251,19 +244,19 @@ } QColor -SingleColourLayer::getBackgroundQColor(View *v) const +SingleColourLayer::getBackgroundQColor(LayerGeometryProvider *v) const { return v->getBackground(); } QColor -SingleColourLayer::getForegroundQColor(View *v) const +SingleColourLayer::getForegroundQColor(LayerGeometryProvider *v) const { return v->getForeground(); } std::vector -SingleColourLayer::getPartialShades(View *v) const +SingleColourLayer::getPartialShades(LayerGeometryProvider *v) const { std::vector s; QColor base = getBaseQColor(); @@ -307,6 +300,9 @@ int colour = ColourDatabase::getInstance()->putStringValues (colourName, colourSpec, darkbg); + if (colour == -1) + return; + m_colourExplicitlySet = true; if (m_colour != colour) { @@ -315,20 +311,27 @@ SVDEBUG << "SingleColourLayer::setProperties: changing colour from " << m_colour << " to " << colour << endl; #endif - if (m_colourRefCount.find(m_colour) != m_colourRefCount.end() && - m_colourRefCount[m_colour] > 0) { - m_colourRefCount[m_colour]--; - } - + unrefColor(); m_colour = colour; - - if (m_colourRefCount.find(m_colour) == m_colourRefCount.end()) { - m_colourRefCount[m_colour] = 1; - } else { - m_colourRefCount[m_colour]++; - } + refColor(); flagBaseColourChanged(); } } +void SingleColourLayer::refColor() +{ + if (m_colourRefCount.find(m_colour) == m_colourRefCount.end()) { + m_colourRefCount[m_colour] = 1; + } else { + m_colourRefCount[m_colour]++; + } +} + +void SingleColourLayer::unrefColor() +{ + if (m_colourRefCount.find(m_colour) != m_colourRefCount.end() && + m_colourRefCount[m_colour] > 0) { + m_colourRefCount[m_colour]--; + } +} diff -r 282f4be8f058 -r c02c51ae5238 layer/SingleColourLayer.h --- a/layer/SingleColourLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/SingleColourLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -71,15 +71,16 @@ virtual void setProperties(const QXmlAttributes &attributes); - virtual void setDefaultColourFor(View *v); + virtual void setDefaultColourFor(LayerGeometryProvider *v); protected: SingleColourLayer(); + virtual ~SingleColourLayer(); virtual QColor getBaseQColor() const; - virtual QColor getBackgroundQColor(View *v) const; - virtual QColor getForegroundQColor(View *v) const; - std::vector getPartialShades(View *v) const; + virtual QColor getBackgroundQColor(LayerGeometryProvider *v) const; + virtual QColor getForegroundQColor(LayerGeometryProvider *v) const; + std::vector getPartialShades(LayerGeometryProvider *v) const; virtual void flagBaseColourChanged() { } virtual int getDefaultColourHint(bool /* darkBackground */, @@ -91,6 +92,10 @@ int m_colour; bool m_colourExplicitlySet; bool m_defaultColourSet; + +private: + void refColor(); + void unrefColor(); }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/SliceLayer.cpp --- a/layer/SliceLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/SliceLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -92,23 +92,23 @@ } QString -SliceLayer::getFeatureDescription(View *v, QPoint &p) const +SliceLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &p) const { int minbin, maxbin, range; - return getFeatureDescription(v, p, true, minbin, maxbin, range); + return getFeatureDescriptionAux(v, p, true, minbin, maxbin, range); } QString -SliceLayer::getFeatureDescription(View *v, QPoint &p, - bool includeBinDescription, - int &minbin, int &maxbin, int &range) const +SliceLayer::getFeatureDescriptionAux(LayerGeometryProvider *v, QPoint &p, + bool includeBinDescription, + int &minbin, int &maxbin, int &range) const { minbin = 0; maxbin = 0; if (!m_sliceableModel) return ""; int xorigin = m_xorigins[v]; - int w = v->width() - xorigin - 1; + int w = v->getPaintWidth() - xorigin - 1; int mh = m_sliceableModel->getHeight(); minbin = getBinForX(p.x() - xorigin, mh, w); @@ -119,21 +119,21 @@ if (minbin < 0) minbin = 0; if (maxbin < 0) maxbin = 0; - int sampleRate = m_sliceableModel->getSampleRate(); + sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - size_t f0 = m_currentf0; - size_t f1 = m_currentf1; + sv_frame_t f0 = m_currentf0; + sv_frame_t f1 = m_currentf1; RealTime rt0 = RealTime::frame2RealTime(f0, sampleRate); RealTime rt1 = RealTime::frame2RealTime(f1, sampleRate); - range = f1 - f0 + 1; + range = int(f1 - f0 + 1); QString rtrangestr = QString("%1 s").arg((rt1 - rt0).toText().c_str()); if (includeBinDescription) { - float minvalue = 0.f; + float minvalue = 0.0; if (minbin < int(m_values.size())) minvalue = m_values[minbin]; float maxvalue = minvalue; @@ -179,23 +179,23 @@ } } -float -SliceLayer::getXForBin(int bin, int count, float w) const +double +SliceLayer::getXForBin(int bin, int count, double w) const { - float x = 0; + double x = 0; switch (m_binScale) { case LinearBins: - x = (float(w) * bin) / count; + x = (w * bin) / count; break; case LogBins: - x = (float(w) * log10f(bin + 1)) / log10f(count + 1); + x = (w * log10(bin + 1)) / log10(count + 1); break; case InvertedLogBins: - x = w - (float(w) * log10f(count - bin - 1)) / log10f(count); + x = w - (w * log10(count - bin - 1)) / log10(count); break; } @@ -203,7 +203,7 @@ } int -SliceLayer::getBinForX(float x, int count, float w) const +SliceLayer::getBinForX(double x, int count, double w) const { int bin = 0; @@ -214,21 +214,21 @@ break; case LogBins: - bin = int(powf(10.f, (x * log10f(count + 1)) / w) - 1 + 0.0001); + bin = int(pow(10.0, (x * log10(count + 1)) / w) - 1 + 0.0001); break; case InvertedLogBins: - bin = count + 1 - int(powf(10.f, (log10f(count) * (w - x)) / float(w)) + 0.0001); + bin = count + 1 - int(pow(10.0, (log10(count) * (w - x)) / double(w)) + 0.0001); break; } return bin; } -float -SliceLayer::getYForValue(float value, const View *v, float &norm) const +double +SliceLayer::getYForValue(double value, const LayerGeometryProvider *v, double &norm) const { - norm = 0.f; + norm = 0.0; if (m_yorigins.find(v) == m_yorigins.end()) return 0; @@ -236,9 +236,9 @@ int yorigin = m_yorigins[v]; int h = m_heights[v]; - float thresh = getThresholdDb(); + double thresh = getThresholdDb(); - float y = 0.f; + double y = 0.0; if (h <= 0) return y; @@ -246,44 +246,45 @@ case dBScale: { - float db = thresh; - if (value > 0.f) db = 10.f * log10f(fabsf(value)); + double db = thresh; + if (value > 0.0) db = 10.0 * log10(fabs(value)); if (db < thresh) db = thresh; norm = (db - thresh) / -thresh; - y = yorigin - (float(h) * norm); + y = yorigin - (double(h) * norm); break; } case MeterScale: y = AudioLevel::multiplier_to_preview(value, h); - norm = float(y) / float(h); + norm = double(y) / double(h); y = yorigin - y; break; case AbsoluteScale: - value = fabsf(value); + value = fabs(value); // and fall through + case LinearScale: default: norm = (value - m_threshold); if (norm < 0) norm = 0; - y = yorigin - (float(h) * norm); + y = yorigin - (double(h) * norm); break; } return y; } -float -SliceLayer::getValueForY(float y, const View *v) const +double +SliceLayer::getValueForY(double y, const LayerGeometryProvider *v) const { - float value = 0.f; + double value = 0.0; if (m_yorigins.find(v) == m_yorigins.end()) return value; int yorigin = m_yorigins[v]; int h = m_heights[v]; - float thresh = getThresholdDb(); + double thresh = getThresholdDb(); if (h <= 0) return value; @@ -293,15 +294,17 @@ case dBScale: { - float db = ((y / h) * -thresh) + thresh; - value = powf(10.f, db/10.f); + double db = ((y / h) * -thresh) + thresh; + value = pow(10.0, db/10.0); break; } case MeterScale: - value = AudioLevel::preview_to_multiplier(lrintf(y), h); + value = AudioLevel::preview_to_multiplier(int(lrint(y)), h); break; - + + case LinearScale: + case AbsoluteScale: default: value = y / h + m_threshold; } @@ -310,7 +313,7 @@ } void -SliceLayer::paint(View *v, QPainter &paint, QRect rect) const +SliceLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_sliceableModel || !m_sliceableModel->isOK() || !m_sliceableModel->isReady()) return; @@ -322,7 +325,7 @@ if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) { if (!m_scalePoints.empty()) { paint.setPen(QColor(240, 240, 240)); //!!! and dark background? - for (size_t i = 0; i < m_scalePoints.size(); ++i) { + for (int i = 0; i < (int)m_scalePoints.size(); ++i) { paint.drawLine(0, m_scalePoints[i], rect.width(), m_scalePoints[i]); } } @@ -331,11 +334,11 @@ paint.setPen(getBaseQColor()); int xorigin = getVerticalScaleWidth(v, true, paint) + 1; - int w = v->width() - xorigin - 1; + int w = v->getPaintWidth() - xorigin - 1; m_xorigins[v] = xorigin; // for use in getFeatureDescription - int yorigin = v->height() - 20 - paint.fontMetrics().height() - 7; + int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 7; int h = yorigin - paint.fontMetrics().height() - 8; m_yorigins[v] = yorigin; // for getYForValue etc @@ -345,27 +348,27 @@ QPainterPath path; - size_t mh = m_sliceableModel->getHeight(); + int mh = m_sliceableModel->getHeight(); int divisor = 0; m_values.clear(); - for (size_t bin = 0; bin < mh; ++bin) { - m_values.push_back(0.f); + for (int bin = 0; bin < mh; ++bin) { + m_values.push_back(0.0); } - size_t f0 = v->getCentreFrame(); + sv_frame_t f0 = v->getCentreFrame(); int f0x = v->getXForFrame(f0); f0 = v->getFrameForX(f0x); - size_t f1 = v->getFrameForX(f0x + 1); + sv_frame_t f1 = v->getFrameForX(f0x + 1); if (f1 > f0) --f1; // cerr << "centre frame " << v->getCentreFrame() << ", x " << f0x << ", f0 " << f0 << ", f1 " << f1 << endl; - size_t res = m_sliceableModel->getResolution(); - size_t col0 = f0 / res; - size_t col1 = col0; - if (m_samplingMode != NearestSample) col1 = f1 / res; + int res = m_sliceableModel->getResolution(); + int col0 = int(f0 / res); + int col1 = col0; + if (m_samplingMode != NearestSample) col1 = int(f1 / res); f0 = col0 * res; f1 = (col1 + 1) * res - 1; @@ -376,10 +379,10 @@ BiasCurve curve; getBiasCurve(curve); - size_t cs = curve.size(); + int cs = int(curve.size()); - for (size_t col = col0; col <= col1; ++col) { - for (size_t bin = 0; bin < mh; ++bin) { + for (int col = col0; col <= col1; ++col) { + for (int bin = 0; bin < mh; ++bin) { float value = m_sliceableModel->getValueAt(col, bin); if (bin < cs) value *= curve[bin]; if (m_samplingMode == SamplePeak) { @@ -391,30 +394,31 @@ ++divisor; } - float max = 0.f; - for (size_t bin = 0; bin < mh; ++bin) { - if (m_samplingMode == SampleMean) m_values[bin] /= divisor; + float max = 0.0; + for (int bin = 0; bin < mh; ++bin) { + if (m_samplingMode == SampleMean && divisor > 0) { + m_values[bin] /= float(divisor); + } if (m_values[bin] > max) max = m_values[bin]; } - if (max != 0.f && m_normalize) { - for (size_t bin = 0; bin < mh; ++bin) { + if (max != 0.0 && m_normalize) { + for (int bin = 0; bin < mh; ++bin) { m_values[bin] /= max; } } - float py = 0; - float nx = xorigin; + double nx = xorigin; ColourMapper mapper(m_colourMap, 0, 1); - for (size_t bin = 0; bin < mh; ++bin) { + for (int bin = 0; bin < mh; ++bin) { - float x = nx; + double x = nx; nx = xorigin + getXForBin(bin + 1, mh, w); - float value = m_values[bin]; - float norm = 0.f; - float y = getYForValue(value, v, norm); + double value = m_values[bin]; + double norm = 0.0; + double y = getYForValue(value, v, norm); if (m_plotStyle == PlotLines) { @@ -446,7 +450,6 @@ paint.fillRect(QRectF(x, y, nx - x, yorigin - y), mapper.map(norm)); } - py = y; } if (m_plotStyle != PlotFilledBlocks) { @@ -498,7 +501,7 @@ } int -SliceLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const +SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const { if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) { return std::max(paint.fontMetrics().width("0.0") + 13, @@ -510,9 +513,9 @@ } void -SliceLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const +SliceLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const { - float thresh = m_threshold; + double thresh = m_threshold; if (m_energyScale != LinearScale && m_energyScale != AbsoluteScale) { thresh = AudioLevel::dB_to_multiplier(getThresholdDb()); } @@ -520,7 +523,7 @@ // int h = (rect.height() * 3) / 4; // int y = (rect.height() / 2) - (h / 2); - int yorigin = v->height() - 20 - paint.fontMetrics().height() - 6; + int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 6; int h = yorigin - paint.fontMetrics().height() - 8; if (h < 0) return; @@ -535,7 +538,7 @@ const_cast *>(&m_scalePoints)); if (mult != 1 && mult != 0) { - int log = lrintf(log10f(mult)); + int log = int(lrint(log10(mult))); QString a = tr("x10"); QString b = QString("%1").arg(-log); paint.drawText(3, 8 + paint.fontMetrics().ascent(), a); @@ -624,7 +627,7 @@ cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl; - val = lrint(log10(m_gain) * 20.0); + val = int(lrint(log10(m_gain) * 20.0)); if (val < *min) val = *min; if (val > *max) val = *max; @@ -633,11 +636,11 @@ *min = -80; *max = 0; - *deflt = lrintf(AudioLevel::multiplier_to_dB(m_initialThreshold)); + *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); if (*deflt < *min) *deflt = *min; if (*deflt > *max) *deflt = *max; - val = lrintf(AudioLevel::multiplier_to_dB(m_threshold)); + val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); if (val < *min) val = *min; if (val > *max) val = *max; @@ -754,10 +757,10 @@ SliceLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(pow(10, float(value)/20.0)); + setGain(powf(10, float(value)/20.0f)); } else if (name == "Threshold") { - if (value == -80) setThreshold(0.0); - else setThreshold(AudioLevel::dB_to_multiplier(value)); + if (value == -80) setThreshold(0.0f); + else setThreshold(float(AudioLevel::dB_to_multiplier(value))); } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) { setFillColourMap(value); } else if (name == "Scale") { @@ -864,7 +867,7 @@ SliceLayer::getThresholdDb() const { if (m_threshold == 0.0) return -80.f; - float db = AudioLevel::multiplier_to_dB(m_threshold); + float db = float(AudioLevel::multiplier_to_dB(m_threshold)); return db; } @@ -939,7 +942,7 @@ } bool -SliceLayer::getValueExtents(float &, float &, bool &, QString &) const +SliceLayer::getValueExtents(double &, double &, bool &, QString &) const { return false; } diff -r 282f4be8f058 -r c02c51ae5238 layer/SliceLayer.h --- a/layer/SliceLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/SliceLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -38,12 +38,12 @@ void setSliceableModel(const Model *model); - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; - virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const; - virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const; + virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const; + virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const; virtual ColourSignificance getLayerColourSignificance() const { return ColourAndBackgroundSignificant; @@ -62,12 +62,12 @@ virtual void setProperty(const PropertyName &, int value); virtual void setProperties(const QXmlAttributes &); - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const; virtual bool hasTimeXAxis() const { return false; } - virtual bool isLayerScrollable(const View *) const { return false; } + virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; } enum EnergyScale { LinearScale, MeterScale, dBScale, AbsoluteScale }; @@ -93,7 +93,7 @@ BinScale getBinScale() const { return m_binScale; } void setThreshold(float); - int getThreshold() const { return m_threshold; } + float getThreshold() const { return m_threshold; } void setGain(float gain); float getGain() const; @@ -109,16 +109,16 @@ void modelAboutToBeDeleted(Model *); protected: - virtual float getXForBin(int bin, int totalBins, float w) const; - virtual int getBinForX(float x, int totalBins, float w) const; + virtual double getXForBin(int bin, int totalBins, double w) const; + virtual int getBinForX(double x, int totalBins, double w) const; - virtual float getYForValue(float value, const View *v, float &norm) const; - virtual float getValueForY(float y, const View *v) const; + virtual double getYForValue(double value, const LayerGeometryProvider *v, double &norm) const; + virtual double getValueForY(double y, const LayerGeometryProvider *v) const; - virtual QString getFeatureDescription(View *v, QPoint &, - bool includeBinDescription, - int &minbin, int &maxbin, - int &range) const; + virtual QString getFeatureDescriptionAux(LayerGeometryProvider *v, QPoint &, + bool includeBinDescription, + int &minbin, int &maxbin, + int &range) const; // This curve may, of course, be flat -- the spectrum uses it for // normalizing the fft results by the fft size (with 1/(fftsize/2) @@ -141,11 +141,11 @@ float m_initialThreshold; float m_gain; mutable std::vector m_scalePoints; - mutable std::map m_xorigins; - mutable std::map m_yorigins; - mutable std::map m_heights; - mutable size_t m_currentf0; - mutable size_t m_currentf1; + mutable std::map m_xorigins; + mutable std::map m_yorigins; + mutable std::map m_heights; + mutable sv_frame_t m_currentf0; + mutable sv_frame_t m_currentf1; mutable std::vector m_values; }; diff -r 282f4be8f058 -r c02c51ae5238 layer/SpectrogramLayer.cpp --- a/layer/SpectrogramLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/SpectrogramLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -33,16 +33,14 @@ #include #include #include -#include #include #include #include #include +#include #include - - #include #include @@ -50,7 +48,9 @@ #include #endif -//#define DEBUG_SPECTROGRAM_REPAINT 1 +#define DEBUG_SPECTROGRAM_REPAINT 1 + +using namespace std; SpectrogramLayer::SpectrogramLayer(Configuration config) : m_model(0), @@ -73,17 +73,16 @@ m_colourMap(0), m_frequencyScale(LinearFrequencyScale), m_binDisplay(AllBins), - m_normalizeColumns(false), - m_normalizeVisibleArea(false), + m_normalization(NoNormalization), m_lastEmittedZoomStep(-1), m_synchronous(false), m_haveDetailedScale(false), - m_lastPaintBlockWidth(0), - m_updateTimer(0), - m_candidateFillStartFrame(0), m_exiting(false), m_sliceableModel(0) { + QString colourConfigName = "spectrogram-colour"; + int colourConfigDefault = int(ColourMapper::Green); + if (config == FullRangeDb) { m_initialMaxFrequency = 0; setMaxFrequency(0); @@ -96,6 +95,8 @@ setColourScale(LinearColourScale); setColourMap(ColourMapper::Sunset); setFrequencyScale(LogFrequencyScale); + colourConfigName = "spectrogram-melodic-colour"; + colourConfigDefault = int(ColourMapper::Sunset); // setGain(20); } else if (config == MelodicPeaks) { setWindowSize(4096); @@ -106,9 +107,16 @@ setFrequencyScale(LogFrequencyScale); setColourScale(LinearColourScale); setBinDisplay(PeakFrequencies); - setNormalizeColumns(true); + setNormalization(NormalizeColumns); + colourConfigName = "spectrogram-melodic-colour"; + colourConfigDefault = int(ColourMapper::Sunset); } + QSettings settings; + settings.beginGroup("Preferences"); + setColourMap(settings.value(colourConfigName, colourConfigDefault).toInt()); + settings.endGroup(); + Preferences *prefs = Preferences::getInstance(); connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); @@ -119,9 +127,6 @@ SpectrogramLayer::~SpectrogramLayer() { - delete m_updateTimer; - m_updateTimer = 0; - invalidateFFTModels(); } @@ -140,8 +145,8 @@ connectSignals(m_model); connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid())); - connect(m_model, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(cacheInvalid(size_t, size_t))); + connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), + this, SLOT(cacheInvalid(sv_frame_t, sv_frame_t))); emit modelReplaced(); } @@ -154,8 +159,7 @@ list.push_back("Colour Scale"); list.push_back("Window Size"); list.push_back("Window Increment"); - list.push_back("Normalize Columns"); - list.push_back("Normalize Visible Area"); + list.push_back("Normalization"); list.push_back("Bin Display"); list.push_back("Threshold"); list.push_back("Gain"); @@ -174,8 +178,7 @@ if (name == "Colour Scale") return tr("Colour Scale"); if (name == "Window Size") return tr("Window Size"); if (name == "Window Increment") return tr("Window Overlap"); - if (name == "Normalize Columns") return tr("Normalize Columns"); - if (name == "Normalize Visible Area") return tr("Normalize Visible Area"); + if (name == "Normalization") return tr("Normalization"); if (name == "Bin Display") return tr("Bin Display"); if (name == "Threshold") return tr("Threshold"); if (name == "Gain") return tr("Gain"); @@ -188,10 +191,8 @@ } QString -SpectrogramLayer::getPropertyIconName(const PropertyName &name) const +SpectrogramLayer::getPropertyIconName(const PropertyName &) const { - if (name == "Normalize Columns") return "normalise-columns"; - if (name == "Normalize Visible Area") return "normalise"; return ""; } @@ -200,8 +201,6 @@ { if (name == "Gain") return RangeProperty; if (name == "Colour Rotation") return RangeProperty; - if (name == "Normalize Columns") return ToggleProperty; - if (name == "Normalize Visible Area") return ToggleProperty; if (name == "Threshold") return RangeProperty; if (name == "Zero Padding") return ToggleProperty; return ValueProperty; @@ -218,8 +217,7 @@ if (name == "Colour" || name == "Threshold" || name == "Colour Rotation") return tr("Colour"); - if (name == "Normalize Columns" || - name == "Normalize Visible Area" || + if (name == "Normalization" || name == "Gain" || name == "Colour Scale") return tr("Scale"); return QString(); @@ -241,11 +239,11 @@ *min = -50; *max = 50; - *deflt = lrintf(log10(m_initialGain) * 20.0);; + *deflt = int(lrint(log10(m_initialGain) * 20.0)); if (*deflt < *min) *deflt = *min; if (*deflt > *max) *deflt = *max; - val = lrintf(log10(m_gain) * 20.0); + val = int(lrint(log10(m_gain) * 20.0)); if (val < *min) val = *min; if (val > *max) val = *max; @@ -254,11 +252,11 @@ *min = -50; *max = 0; - *deflt = lrintf(AudioLevel::multiplier_to_dB(m_initialThreshold)); + *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); if (*deflt < *min) *deflt = *min; if (*deflt > *max) *deflt = *max; - val = lrintf(AudioLevel::multiplier_to_dB(m_threshold)); + val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); if (val < *min) val = *min; if (val > *max) val = *max; @@ -364,15 +362,12 @@ *deflt = int(AllBins); val = (int)m_binDisplay; - } else if (name == "Normalize Columns") { + } else if (name == "Normalization") { - *deflt = 0; - val = (m_normalizeColumns ? 1 : 0); - - } else if (name == "Normalize Visible Area") { - - *deflt = 0; - val = (m_normalizeVisibleArea ? 1 : 0); + *min = 0; + *max = 3; + *deflt = int(NoNormalization); + val = (int)m_normalization; } else { val = Layer::getPropertyRangeAndValue(name, min, max, deflt); @@ -398,6 +393,9 @@ case 4: return tr("Phase"); } } + if (name == "Normalization") { + return ""; // icon only + } if (name == "Window Size") { return QString("%1").arg(32 << value); } @@ -464,6 +462,22 @@ return tr(""); } +QString +SpectrogramLayer::getPropertyValueIconName(const PropertyName &name, + int value) const +{ + if (name == "Normalization") { + switch(value) { + default: + case 0: return "normalise-none"; + case 1: return "normalise-columns"; + case 2: return "normalise"; + case 3: return "normalise-hybrid"; + } + } + return ""; +} + RangeMapper * SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const { @@ -480,10 +494,10 @@ SpectrogramLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(pow(10, float(value)/20.0)); + setGain(float(pow(10, float(value)/20.0))); } else if (name == "Threshold") { if (value == -50) setThreshold(0.0); - else setThreshold(AudioLevel::dB_to_multiplier(value)); + else setThreshold(float(AudioLevel::dB_to_multiplier(value))); } else if (name == "Colour Rotation") { setColourRotation(value); } else if (name == "Colour") { @@ -554,86 +568,26 @@ case 1: setBinDisplay(PeakBins); break; case 2: setBinDisplay(PeakFrequencies); break; } - } else if (name == "Normalize Columns") { - setNormalizeColumns(value ? true : false); - } else if (name == "Normalize Visible Area") { - setNormalizeVisibleArea(value ? true : false); + } else if (name == "Normalization") { + switch (value) { + default: + case 0: setNormalization(NoNormalization); break; + case 1: setNormalization(NormalizeColumns); break; + case 2: setNormalization(NormalizeVisibleArea); break; + case 3: setNormalization(NormalizeHybrid); break; + } } } void SpectrogramLayer::invalidateImageCaches() { +#ifdef DEBUG_SPECTROGRAM + cerr << "SpectrogramLayer::invalidateImageCaches called" << endl; +#endif for (ViewImageCache::iterator i = m_imageCaches.begin(); i != m_imageCaches.end(); ++i) { - i->second.validArea = QRect(); - } -} - -void -SpectrogramLayer::invalidateImageCaches(size_t startFrame, size_t endFrame) -{ - for (ViewImageCache::iterator i = m_imageCaches.begin(); - i != m_imageCaches.end(); ++i) { - - //!!! when are views removed from the map? on setLayerDormant? - const View *v = i->first; - -#ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::invalidateImageCaches(" - << startFrame << ", " << endFrame << "): view range is " - << v->getStartFrame() << ", " << v->getEndFrame() - << endl; - - cerr << "Valid area was: " << i->second.validArea.x() << ", " - << i->second.validArea.y() << " " - << i->second.validArea.width() << "x" - << i->second.validArea.height() << endl; -#endif - - if (long(startFrame) > v->getStartFrame()) { - if (startFrame >= v->getEndFrame()) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Modified start frame is off right of view" << endl; -#endif - return; - } - int x = v->getXForFrame(startFrame); -#ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "clipping from 0 to " << x-1 << endl; -#endif - if (x > 1) { - i->second.validArea &= - QRect(0, 0, x-1, v->height()); - } else { - i->second.validArea = QRect(); - } - } else { - if (long(endFrame) < v->getStartFrame()) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Modified end frame is off left of view" << endl; -#endif - return; - } - int x = v->getXForFrame(endFrame); -#ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "clipping from " << x+1 << " to " << v->width() - << endl; -#endif - if (x < v->width()) { - i->second.validArea &= - QRect(x+1, 0, v->width()-(x+1), v->height()); - } else { - i->second.validArea = QRect(); - } - } - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Valid area is now: " << i->second.validArea.x() << ", " - << i->second.validArea.y() << " " - << i->second.validArea.width() << "x" - << i->second.validArea.height() << endl; -#endif + i->second.invalidate(); } } @@ -680,7 +634,7 @@ } void -SpectrogramLayer::setWindowSize(size_t ws) +SpectrogramLayer::setWindowSize(int ws) { if (m_windowSize == ws) return; @@ -694,14 +648,14 @@ emit layerParametersChanged(); } -size_t +int SpectrogramLayer::getWindowSize() const { return m_windowSize; } void -SpectrogramLayer::setWindowHopLevel(size_t v) +SpectrogramLayer::setWindowHopLevel(int v) { if (m_windowHopLevel == v) return; @@ -716,14 +670,14 @@ // fillCache(); } -size_t +int SpectrogramLayer::getWindowHopLevel() const { return m_windowHopLevel; } void -SpectrogramLayer::setZeroPadLevel(size_t v) +SpectrogramLayer::setZeroPadLevel(int v) { if (m_zeroPadLevel == v) return; @@ -737,7 +691,7 @@ emit layerParametersChanged(); } -size_t +int SpectrogramLayer::getZeroPadLevel() const { return m_zeroPadLevel; @@ -803,7 +757,7 @@ } void -SpectrogramLayer::setMinFrequency(size_t mf) +SpectrogramLayer::setMinFrequency(int mf) { if (m_minFrequency == mf) return; @@ -817,14 +771,14 @@ emit layerParametersChanged(); } -size_t +int SpectrogramLayer::getMinFrequency() const { return m_minFrequency; } void -SpectrogramLayer::setMaxFrequency(size_t mf) +SpectrogramLayer::setMaxFrequency(int mf) { if (m_maxFrequency == mf) return; @@ -838,7 +792,7 @@ emit layerParametersChanged(); } -size_t +int SpectrogramLayer::getMaxFrequency() const { return m_maxFrequency; @@ -933,51 +887,30 @@ } void -SpectrogramLayer::setNormalizeColumns(bool n) +SpectrogramLayer::setNormalization(Normalization n) { - if (m_normalizeColumns == n) return; + if (m_normalization == n) return; invalidateImageCaches(); invalidateMagnitudes(); - m_normalizeColumns = n; + m_normalization = n; emit layerParametersChanged(); } -bool -SpectrogramLayer::getNormalizeColumns() const +SpectrogramLayer::Normalization +SpectrogramLayer::getNormalization() const { - return m_normalizeColumns; + return m_normalization; } void -SpectrogramLayer::setNormalizeVisibleArea(bool n) -{ - SVDEBUG << "SpectrogramLayer::setNormalizeVisibleArea(" << n - << ") (from " << m_normalizeVisibleArea << ")" << endl; - - if (m_normalizeVisibleArea == n) return; - - invalidateImageCaches(); - invalidateMagnitudes(); - m_normalizeVisibleArea = n; - - emit layerParametersChanged(); -} - -bool -SpectrogramLayer::getNormalizeVisibleArea() const -{ - return m_normalizeVisibleArea; -} - -void -SpectrogramLayer::setLayerDormant(const View *v, bool dormant) +SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant) { if (dormant) { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::setLayerDormant(" << dormant << ")" + cerr << "SpectrogramLayer::setLayerDormant(" << dormant << ")" << endl; #endif @@ -987,17 +920,20 @@ Layer::setLayerDormant(v, true); + const View *view = v->getView(); + invalidateImageCaches(); - m_imageCaches.erase(v); - - if (m_fftModels.find(v) != m_fftModels.end()) { - - if (m_sliceableModel == m_fftModels[v].first) { + + m_imageCaches.erase(view->getId()); + + if (m_fftModels.find(view->getId()) != m_fftModels.end()) { + + if (m_sliceableModel == m_fftModels[view->getId()]) { bool replaced = false; for (ViewFFTMap::iterator i = m_fftModels.begin(); i != m_fftModels.end(); ++i) { - if (i->second.first != m_sliceableModel) { - emit sliceableModelReplaced(m_sliceableModel, i->second.first); + if (i->second != m_sliceableModel) { + emit sliceableModelReplaced(m_sliceableModel, i->second); replaced = true; break; } @@ -1005,11 +941,11 @@ if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); } - delete m_fftModels[v].first; - m_fftModels.erase(v); - - delete m_peakCaches[v]; - m_peakCaches.erase(v); + delete m_fftModels[view->getId()]; + m_fftModels.erase(view->getId()); + + delete m_peakCaches[view->getId()]; + m_peakCaches.erase(view->getId()); } } else { @@ -1022,7 +958,7 @@ SpectrogramLayer::cacheInvalid() { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::cacheInvalid()" << endl; + cerr << "SpectrogramLayer::cacheInvalid()" << endl; #endif invalidateImageCaches(); @@ -1030,84 +966,28 @@ } void -SpectrogramLayer::cacheInvalid(size_t from, size_t to) +SpectrogramLayer::cacheInvalid( +#ifdef DEBUG_SPECTROGRAM_REPAINT + sv_frame_t from, sv_frame_t to +#else + sv_frame_t , sv_frame_t +#endif + ) { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; + cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; #endif - invalidateImageCaches(from, to); + // We used to call invalidateMagnitudes(from, to) to invalidate + // only those caches whose views contained some of the (from, to) + // range. That's the right thing to do; it has been lost in + // pulling out the image cache code, but it might not matter very + // much, since the underlying models for spectrogram layers don't + // change very often. Let's see. + invalidateImageCaches(); invalidateMagnitudes(); } -void -SpectrogramLayer::fillTimerTimedOut() -{ - if (!m_model) return; - - bool allDone = true; - -#ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << endl; -#endif - - for (ViewFFTMap::iterator i = m_fftModels.begin(); - i != m_fftModels.end(); ++i) { - - const FFTModel *model = i->second.first; - size_t lastFill = i->second.second; - - if (model) { - - size_t fill = model->getFillExtent(); - -#ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << endl; -#endif - - if (fill >= lastFill) { - if (fill >= m_model->getEndFrame() && lastFill > 0) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "complete!" << endl; -#endif - invalidateImageCaches(); - i->second.second = -1; - emit modelChanged(); - - } else if (fill > lastFill) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "SpectrogramLayer: emitting modelChanged(" - << lastFill << "," << fill << ")" << endl; -#endif - invalidateImageCaches(lastFill, fill); - i->second.second = fill; - emit modelChanged(lastFill, fill); - } - } else { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "SpectrogramLayer: going backwards, emitting modelChanged(" - << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << endl; -#endif - invalidateImageCaches(); - i->second.second = fill; - emit modelChanged(m_model->getStartFrame(), m_model->getEndFrame()); - } - - if (i->second.second >= 0) { - allDone = false; - } - } - } - - if (allDone) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "SpectrogramLayer: all complete!" << endl; -#endif - delete m_updateTimer; - m_updateTimer = 0; - } -} - bool SpectrogramLayer::hasLightBackground() const { @@ -1128,7 +1008,7 @@ ColourMapper mapper(m_colourMap, 1.f, 255.f); for (int pixel = 1; pixel < 256; ++pixel) { - m_palette.setColour(pixel, mapper.map(pixel)); + m_palette.setColour((unsigned char)pixel, mapper.map(pixel)); } m_crosshairColour = mapper.getContrastingColour(); @@ -1151,45 +1031,45 @@ int target = pixel + distance; while (target < 1) target += 255; while (target > 255) target -= 255; - newPixels[target] = m_palette.getColour(pixel); + newPixels[target] = m_palette.getColour((unsigned char)pixel); } for (int pixel = 0; pixel < 256; ++pixel) { - m_palette.setColour(pixel, newPixels[pixel]); + m_palette.setColour((unsigned char)pixel, newPixels[pixel]); } m_drawBuffer = QImage(); } unsigned char -SpectrogramLayer::getDisplayValue(View *v, float input) const +SpectrogramLayer::getDisplayValue(LayerGeometryProvider *v, double input) const { int value; - float min = 0.f; - float max = 1.f; - - if (m_normalizeVisibleArea) { - min = m_viewMags[v].getMin(); - max = m_viewMags[v].getMax(); - } else if (!m_normalizeColumns) { + double min = 0.0; + double max = 1.0; + + if (m_normalization == NormalizeVisibleArea) { + min = m_viewMags[v->getId()].getMin(); + max = m_viewMags[v->getId()].getMax(); + } else if (m_normalization != NormalizeColumns) { if (m_colourScale == LinearColourScale //|| // m_colourScale == MeterColourScale) { ) { - max = 0.1f; + max = 0.1; } } - float thresh = -80.f; - - if (max == 0.f) max = 1.f; - if (max == min) min = max - 0.0001f; + double thresh = -80.0; + + if (max == 0.0) max = 1.0; + if (max == min) min = max - 0.0001; switch (m_colourScale) { default: case LinearColourScale: - value = int(((input - min) / (max - min)) * 255.f) + 1; + value = int(((input - min) / (max - min)) * 255.0) + 1; break; case MeterColourScale: @@ -1199,19 +1079,19 @@ case dBSquaredColourScale: input = ((input - min) * (input - min)) / ((max - min) * (max - min)); - if (input > 0.f) { - input = 10.f * log10f(input); + if (input > 0.0) { + input = 10.0 * log10(input); } else { input = thresh; } - if (min > 0.f) { - thresh = 10.f * log10f(min * min); - if (thresh < -80.f) thresh = -80.f; + if (min > 0.0) { + thresh = 10.0 * log10(min * min); + if (thresh < -80.0) thresh = -80.0; } input = (input - thresh) / (-thresh); - if (input < 0.f) input = 0.f; - if (input > 1.f) input = 1.f; - value = int(input * 255.f) + 1; + if (input < 0.0) input = 0.0; + if (input > 1.0) input = 1.0; + value = int(input * 255.0) + 1; break; case dBColourScale: @@ -1219,19 +1099,19 @@ //In any case, we need to have some indication of what the dB //scale is relative to. input = (input - min) / (max - min); - if (input > 0.f) { - input = 10.f * log10f(input); + if (input > 0.0) { + input = 10.0 * log10(input); } else { input = thresh; } - if (min > 0.f) { - thresh = 10.f * log10f(min); - if (thresh < -80.f) thresh = -80.f; + if (min > 0.0) { + thresh = 10.0 * log10(min); + if (thresh < -80.0) thresh = -80.0; } input = (input - thresh) / (-thresh); - if (input < 0.f) input = 0.f; - if (input > 1.f) input = 1.f; - value = int(input * 255.f) + 1; + if (input < 0.0) input = 0.0; + if (input > 1.0) input = 1.0; + value = int(input * 255.0) + 1; break; case PhaseColourScale: @@ -1241,61 +1121,17 @@ if (value > UCHAR_MAX) value = UCHAR_MAX; if (value < 0) value = 0; - return value; + return (unsigned char)value; } -float -SpectrogramLayer::getInputForDisplayValue(unsigned char uc) const -{ - //!!! unused - - int value = uc; - float input; - - //!!! incorrect for normalizing visible area (and also out of date) - - switch (m_colourScale) { - - default: - case LinearColourScale: - input = float(value - 1) / 255.0 / (m_normalizeColumns ? 1 : 50); - break; - - case MeterColourScale: - input = AudioLevel::preview_to_multiplier(value - 1, 255) - / (m_normalizeColumns ? 1.0 : 50.0); - break; - - case dBSquaredColourScale: - input = float(value - 1) / 255.0; - input = (input * 80.0) - 80.0; - input = powf(10.0, input) / 20.0; - value = int(input); - break; - - case dBColourScale: - input = float(value - 1) / 255.0; - input = (input * 80.0) - 80.0; - input = powf(10.0, input) / 20.0; - value = int(input); - break; - - case PhaseColourScale: - input = float(value - 128) * M_PI / 127.0; - break; - } - - return input; -} - -float +double SpectrogramLayer::getEffectiveMinFrequency() const { - int sr = m_model->getSampleRate(); - float minf = float(sr) / m_fftSize; + sv_samplerate_t sr = m_model->getSampleRate(); + double minf = double(sr) / m_fftSize; if (m_minFrequency > 0.0) { - size_t minbin = size_t((double(m_minFrequency) * m_fftSize) / sr + 0.01); + int minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.01); if (minbin < 1) minbin = 1; minf = minbin * sr / m_fftSize; } @@ -1303,14 +1139,14 @@ return minf; } -float +double SpectrogramLayer::getEffectiveMaxFrequency() const { - int sr = m_model->getSampleRate(); - float maxf = float(sr) / 2; + sv_samplerate_t sr = m_model->getSampleRate(); + double maxf = double(sr) / 2; if (m_maxFrequency > 0.0) { - size_t maxbin = size_t((double(m_maxFrequency) * m_fftSize) / sr + 0.1); + int maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; maxf = maxbin * sr / m_fftSize; } @@ -1319,16 +1155,16 @@ } bool -SpectrogramLayer::getYBinRange(View *v, int y, float &q0, float &q1) const +SpectrogramLayer::getYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const { Profiler profiler("SpectrogramLayer::getYBinRange"); - int h = v->height(); + int h = v->getPaintHeight(); if (y < 0 || y >= h) return false; - int sr = m_model->getSampleRate(); - float minf = getEffectiveMinFrequency(); - float maxf = getEffectiveMaxFrequency(); + sv_samplerate_t sr = m_model->getSampleRate(); + double minf = getEffectiveMinFrequency(); + double maxf = getEffectiveMaxFrequency(); bool logarithmic = (m_frequencyScale == LogFrequencyScale); @@ -1345,16 +1181,16 @@ } bool -SpectrogramLayer::getSmoothedYBinRange(View *v, int y, float &q0, float &q1) const +SpectrogramLayer::getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const { Profiler profiler("SpectrogramLayer::getSmoothedYBinRange"); - int h = v->height(); + int h = v->getPaintHeight(); if (y < 0 || y >= h) return false; - int sr = m_model->getSampleRate(); - float minf = getEffectiveMinFrequency(); - float maxf = getEffectiveMaxFrequency(); + sv_samplerate_t sr = m_model->getSampleRate(); + double minf = getEffectiveMinFrequency(); + double maxf = getEffectiveMaxFrequency(); bool logarithmic = (m_frequencyScale == LogFrequencyScale); @@ -1371,14 +1207,14 @@ } bool -SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const +SpectrogramLayer::getXBinRange(LayerGeometryProvider *v, int x, double &s0, double &s1) const { - size_t modelStart = m_model->getStartFrame(); - size_t modelEnd = m_model->getEndFrame(); + sv_frame_t modelStart = m_model->getStartFrame(); + sv_frame_t modelEnd = m_model->getEndFrame(); // Each pixel column covers an exact range of sample frames: - int f0 = v->getFrameForX(x) - modelStart; - int f1 = v->getFrameForX(x + 1) - modelStart - 1; + sv_frame_t f0 = v->getFrameForX(x) - modelStart; + sv_frame_t f1 = v->getFrameForX(x + 1) - modelStart - 1; if (f1 < int(modelStart) || f0 > int(modelEnd)) { return false; @@ -1387,17 +1223,17 @@ // And that range may be drawn from a possibly non-integral // range of spectrogram windows: - size_t windowIncrement = getWindowIncrement(); - s0 = float(f0) / windowIncrement; - s1 = float(f1) / windowIncrement; + int windowIncrement = getWindowIncrement(); + s0 = double(f0) / windowIncrement; + s1 = double(f1) / windowIncrement; return true; } bool -SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const +SpectrogramLayer::getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &min, RealTime &max) const { - float s0 = 0, s1 = 0; + double s0 = 0, s1 = 0; if (!getXBinRange(v, x, s0, s1)) return false; int s0i = int(s0 + 0.001); @@ -1414,16 +1250,16 @@ } bool -SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) +SpectrogramLayer::getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) const { - float q0 = 0, q1 = 0; + double q0 = 0, q1 = 0; if (!getYBinRange(v, y, q0, q1)) return false; int q0i = int(q0 + 0.001); int q1i = int(q1); - int sr = m_model->getSampleRate(); + sv_samplerate_t sr = m_model->getSampleRate(); for (int q = q0i; q <= q1i; ++q) { if (q == q0i) freqMin = (sr * q) / m_fftSize; @@ -1433,9 +1269,9 @@ } bool -SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y, - float &freqMin, float &freqMax, - float &adjFreqMin, float &adjFreqMax) +SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, + double &freqMin, double &freqMax, + double &adjFreqMin, double &adjFreqMax) const { if (!m_model || !m_model->isOK() || !m_model->isReady()) { @@ -1445,10 +1281,10 @@ FFTModel *fft = getFFTModel(v); if (!fft) return false; - float s0 = 0, s1 = 0; + double s0 = 0, s1 = 0; if (!getXBinRange(v, x, s0, s1)) return false; - float q0 = 0, q1 = 0; + double q0 = 0, q1 = 0; if (!getYBinRange(v, y, q0, q1)) return false; int s0i = int(s0 + 0.001); @@ -1457,10 +1293,7 @@ int q0i = int(q0 + 0.001); int q1i = int(q1); - int sr = m_model->getSampleRate(); - - size_t windowSize = m_windowSize; - size_t windowIncrement = getWindowIncrement(); + sv_samplerate_t sr = m_model->getSampleRate(); bool haveAdj = false; @@ -1471,18 +1304,15 @@ for (int s = s0i; s <= s1i; ++s) { - if (!fft->isColumnAvailable(s)) continue; - - float binfreq = (sr * q) / m_windowSize; + double binfreq = (double(sr) * q) / m_windowSize; if (q == q0i) freqMin = binfreq; if (q == q1i) freqMax = binfreq; if (peaksOnly && !fft->isLocalPeak(s, q)) continue; - if (!fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) continue; - - float freq = binfreq; - bool steady = false; + if (!fft->isOverThreshold(s, q, float(m_threshold * double(m_fftSize)/2.0))) continue; + + double freq = binfreq; if (s < int(fft->getWidth()) - 1) { @@ -1504,18 +1334,18 @@ } bool -SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y, - float &min, float &max, - float &phaseMin, float &phaseMax) const +SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, + double &min, double &max, + double &phaseMin, double &phaseMax) const { if (!m_model || !m_model->isOK() || !m_model->isReady()) { return false; } - float q0 = 0, q1 = 0; + double q0 = 0, q1 = 0; if (!getYBinRange(v, y, q0, q1)) return false; - float s0 = 0, s1 = 0; + double s0 = 0, s1 = 0; if (!getXBinRange(v, x, s0, s1)) return false; int q0i = int(q0 + 0.001); @@ -1526,7 +1356,7 @@ bool rv = false; - size_t zp = getZeroPadLevel(v); + int zp = getZeroPadLevel(v); q0i *= zp + 1; q1i *= zp + 1; @@ -1547,15 +1377,13 @@ for (int s = s0i; s <= s1i; ++s) { if (s >= 0 && q >= 0 && s < cw && q < ch) { - if (!fft->isColumnAvailable(s)) continue; - - float value; + double value; value = fft->getPhaseAt(s, q); if (!have || value < phaseMin) { phaseMin = value; } if (!have || value > phaseMax) { phaseMax = value; } - value = fft->getMagnitudeAt(s, q) / (m_fftSize/2); + value = fft->getMagnitudeAt(s, q) / (m_fftSize/2.0); if (!have || value < min) { min = value; } if (!have || value > max) { max = value; } @@ -1572,8 +1400,8 @@ return rv; } -size_t -SpectrogramLayer::getZeroPadLevel(const View *v) const +int +SpectrogramLayer::getZeroPadLevel(const LayerGeometryProvider *v) const { //!!! tidy all this stuff @@ -1587,24 +1415,24 @@ if (m_frequencyScale == LogFrequencyScale) return 3; - int sr = m_model->getSampleRate(); + sv_samplerate_t sr = m_model->getSampleRate(); - size_t maxbin = m_fftSize / 2; + int maxbin = m_fftSize / 2; if (m_maxFrequency > 0) { maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; } - size_t minbin = 1; + int minbin = 1; if (m_minFrequency > 0) { minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1); if (minbin < 1) minbin = 1; if (minbin >= maxbin) minbin = maxbin - 1; } - float perPixel = - float(v->height()) / - float((maxbin - minbin) / (m_zeroPadLevel + 1)); + double perPixel = + double(v->getPaintHeight()) / + double((maxbin - minbin) / (m_zeroPadLevel + 1)); if (perPixel > 2.8) { return 3; // 4x oversampling @@ -1615,53 +1443,52 @@ } } -size_t -SpectrogramLayer::getFFTSize(const View *v) const +int +SpectrogramLayer::getFFTSize(const LayerGeometryProvider *v) const { return m_fftSize * (getZeroPadLevel(v) + 1); } FFTModel * -SpectrogramLayer::getFFTModel(const View *v) const +SpectrogramLayer::getFFTModel(const LayerGeometryProvider *v) const { if (!m_model) return 0; - size_t fftSize = getFFTSize(v); - - if (m_fftModels.find(v) != m_fftModels.end()) { - if (m_fftModels[v].first == 0) { + int fftSize = getFFTSize(v); + + const View *view = v->getView(); + + if (m_fftModels.find(view->getId()) != m_fftModels.end()) { + if (m_fftModels[view->getId()] == 0) { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; + cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; #endif return 0; } - if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) { + if (m_fftModels[view->getId()]->getHeight() != fftSize / 2 + 1) { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; + cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view->getId()]->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; #endif - delete m_fftModels[v].first; - m_fftModels.erase(v); - delete m_peakCaches[v]; - m_peakCaches.erase(v); + delete m_fftModels[view->getId()]; + m_fftModels.erase(view->getId()); + delete m_peakCaches[view->getId()]; + m_peakCaches.erase(view->getId()); } else { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << endl; + cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view->getId()]->getHeight() << endl; #endif - return m_fftModels[v].first; + return m_fftModels[view->getId()]; } } - if (m_fftModels.find(v) == m_fftModels.end()) { + if (m_fftModels.find(view->getId()) == m_fftModels.end()) { FFTModel *model = new FFTModel(m_model, m_channel, m_windowType, m_windowSize, getWindowIncrement(), - fftSize, - true, // polar - StorageAdviser::SpeedCritical, - m_candidateFillStartFrame); + fftSize); if (!model->isOK()) { QMessageBox::critical @@ -1669,7 +1496,7 @@ tr("Failed to create the FFT model for this spectrogram.\n" "There may be insufficient memory or disc space to continue.")); delete model; - m_fftModels[v] = FFTFillPair(0, 0); + m_fftModels[view->getId()] = 0; return 0; } @@ -1681,29 +1508,22 @@ m_sliceableModel = model; } - m_fftModels[v] = FFTFillPair(model, 0); - - model->resume(); - - delete m_updateTimer; - m_updateTimer = new QTimer((SpectrogramLayer *)this); - connect(m_updateTimer, SIGNAL(timeout()), - this, SLOT(fillTimerTimedOut())); - m_updateTimer->start(200); + m_fftModels[view->getId()] = model; } - return m_fftModels[v].first; + return m_fftModels[view->getId()]; } Dense3DModelPeakCache * -SpectrogramLayer::getPeakCache(const View *v) const +SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const { - if (!m_peakCaches[v]) { + const View *view = v->getView(); + if (!m_peakCaches[view->getId()]) { FFTModel *f = getFFTModel(v); if (!f) return 0; - m_peakCaches[v] = new Dense3DModelPeakCache(f, 8); + m_peakCaches[view->getId()] = new Dense3DModelPeakCache(f, 8); } - return m_peakCaches[v]; + return m_peakCaches[view->getId()]; } const Model * @@ -1711,16 +1531,19 @@ { if (m_sliceableModel) return m_sliceableModel; if (m_fftModels.empty()) return 0; - m_sliceableModel = m_fftModels.begin()->second.first; + m_sliceableModel = m_fftModels.begin()->second; return m_sliceableModel; } void SpectrogramLayer::invalidateFFTModels() { +#ifdef DEBUG_SPECTROGRAM + cerr << "SpectrogramLayer::invalidateFFTModels called" << endl; +#endif for (ViewFFTMap::iterator i = m_fftModels.begin(); i != m_fftModels.end(); ++i) { - delete i->second.first; + delete i->second; } for (PeakCacheMap::iterator i = m_peakCaches.begin(); i != m_peakCaches.end(); ++i) { @@ -1740,31 +1563,34 @@ void SpectrogramLayer::invalidateMagnitudes() { +#ifdef DEBUG_SPECTROGRAM + cerr << "SpectrogramLayer::invalidateMagnitudes called" << endl; +#endif m_viewMags.clear(); - for (std::vector::iterator i = m_columnMags.begin(); + for (vector::iterator i = m_columnMags.begin(); i != m_columnMags.end(); ++i) { *i = MagnitudeRange(); } } bool -SpectrogramLayer::updateViewMagnitudes(View *v) const +SpectrogramLayer::updateViewMagnitudes(LayerGeometryProvider *v) const { MagnitudeRange mag; - int x0 = 0, x1 = v->width(); - float s00 = 0, s01 = 0, s10 = 0, s11 = 0; + int x0 = 0, x1 = v->getPaintWidth(); + double s00 = 0, s01 = 0, s10 = 0, s11 = 0; if (!getXBinRange(v, x0, s00, s01)) { - s00 = s01 = m_model->getStartFrame() / getWindowIncrement(); + s00 = s01 = double(m_model->getStartFrame()) / getWindowIncrement(); } if (!getXBinRange(v, x1, s10, s11)) { - s10 = s11 = m_model->getEndFrame() / getWindowIncrement(); + s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); } - int s0 = int(std::min(s00, s10) + 0.0001); - int s1 = int(std::max(s01, s11) + 0.0001); + int s0 = int(min(s00, s10) + 0.0001); + int s1 = int(max(s01, s11) + 0.0001); // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; @@ -1779,13 +1605,15 @@ } #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols " - << s0 << " -> " << s1 << " inclusive" << endl; + cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols " + << s0 << " -> " << s1 << " inclusive" << endl; + cerr << "SpectrogramLayer::updateViewMagnitudes: for view id " << v->getId() + << ": min is " << mag.getMin() << ", max is " << mag.getMax() << endl; #endif if (!mag.isSet()) return false; - if (mag == m_viewMags[v]) return false; - m_viewMags[v] = mag; + if (mag == m_viewMags[v->getId()]) return false; + m_viewMags[v->getId()] = mag; return true; } @@ -1795,23 +1623,27 @@ m_synchronous = synchronous; } +ScrollableImageCache & +SpectrogramLayer::getImageCacheReference(const LayerGeometryProvider *view) const +{ + if (m_imageCaches.find(view->getId()) == m_imageCaches.end()) { + m_imageCaches[view->getId()] = ScrollableImageCache(view); + } + return m_imageCaches.at(view->getId()); +} + void -SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const +SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { - // What a lovely, old-fashioned function this is. - // It's practically FORTRAN 77 in its clarity and linearity. - Profiler profiler("SpectrogramLayer::paint", false); #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << endl; + cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl; - cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; + cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; #endif - long startFrame = v->getStartFrame(); - if (startFrame < 0) m_candidateFillStartFrame = 0; - else m_candidateFillStartFrame = startFrame; + sv_frame_t startFrame = v->getStartFrame(); if (!m_model || !m_model->isOK() || !m_model->isReady()) { return; @@ -1825,317 +1657,142 @@ // is not in the dormancy map at all -- we need it to be present // and accountable for when determining whether we need the cache // in the cache-fill thread above. - //!!! no longer use cache-fill thread + //!!! no inter use cache-fill thread const_cast(this)->Layer::setLayerDormant(v, false); - size_t fftSize = getFFTSize(v); -/* - FFTModel *fft = getFFTModel(v); - if (!fft) { - cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << endl; - return; + int fftSize = getFFTSize(v); + + const View *view = v->getView(); + ScrollableImageCache &cache = getImageCacheReference(view); + +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer::paint(): image cache valid area from " << cache.getValidLeft() << " width " << cache.getValidWidth() << ", height " << cache.getSize().height() << endl; + if (rect.x() + rect.width() + 1 < cache.getValidLeft() || + rect.x() > cache.getValidRight()) { + cerr << "SpectrogramLayer: NOTE: requested rect is not contiguous with cache valid area" << endl; } -*/ - ImageCache &cache = m_imageCaches[v]; - -#ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache. - -validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl; #endif -#ifdef DEBUG_SPECTROGRAM_REPAINT - bool stillCacheing = (m_updateTimer != 0); - SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl; -#endif - int zoomLevel = v->getZoomLevel(); - int x0 = 0; - int x1 = v->width(); - - bool recreateWholeImageCache = true; - - x0 = rect.left(); - x1 = rect.right() + 1; -/* - float xPixelRatio = float(fft->getResolution()) / float(zoomLevel); - cerr << "xPixelRatio = " << xPixelRatio << endl; - if (xPixelRatio < 1.f) xPixelRatio = 1.f; -*/ - if (cache.validArea.width() > 0) { - - int cw = cache.image.width(); - int ch = cache.image.height(); - - if (int(cache.zoomLevel) == zoomLevel && - cw == v->width() && - ch == v->height()) { - - if (v->getXForFrame(cache.startFrame) == - v->getXForFrame(startFrame) && - cache.validArea.x() <= x0 && - cache.validArea.x() + cache.validArea.width() >= x1) { - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "SpectrogramLayer: image cache good" << endl; -#endif - - paint.drawImage(rect, cache.image, rect); - //!!! -// paint.drawImage(v->rect(), cache.image, -// QRect(QPoint(0, 0), cache.image.size())); - - illuminateLocalFeatures(v, paint); - return; - - } else { - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "SpectrogramLayer: image cache partially OK" << endl; -#endif - - recreateWholeImageCache = false; - - int dx = v->getXForFrame(cache.startFrame) - - v->getXForFrame(startFrame); - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl; -#endif - - if (dx != 0 && - dx > -cw && - dx < cw) { - - int dxp = dx; - if (dxp < 0) dxp = -dxp; - int copy = (cw - dxp) * sizeof(QRgb); - for (int y = 0; y < ch; ++y) { - QRgb *line = (QRgb *)cache.image.scanLine(y); - if (dx < 0) { - memmove(line, line + dxp, copy); - } else { - memmove(line + dxp, line, copy); - } - } - - int px = cache.validArea.x(); - int pw = cache.validArea.width(); - - if (dx < 0) { - x0 = cw + dx; - x1 = cw; - px += dx; - if (px < 0) { - pw += px; - px = 0; - if (pw < 0) pw = 0; - } - } else { - x0 = 0; - x1 = dx; - px += dx; - if (px + pw > cw) { - pw = int(cw) - px; - if (pw < 0) pw = 0; - } - } - - cache.validArea = - QRect(px, cache.validArea.y(), - pw, cache.validArea.height()); - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "valid area now " - << px << "," << cache.validArea.y() - << " " << pw << "x" << cache.validArea.height() - << endl; -#endif -/* - paint.drawImage(rect & cache.validArea, - cache.image, - rect & cache.validArea); -*/ - } else if (dx != 0) { - - // we scrolled too far to be of use - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << endl; -#endif - - cache.validArea = QRect(); - recreateWholeImageCache = true; - } - } - } else { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "SpectrogramLayer: image cache useless" << endl; - if (int(cache.zoomLevel) != zoomLevel) { - cerr << "(cache zoomLevel " << cache.zoomLevel - << " != " << zoomLevel << ")" << endl; - } - if (cw != v->width()) { - cerr << "(cache width " << cw - << " != " << v->width(); - } - if (ch != v->height()) { - cerr << "(cache height " << ch - << " != " << v->height(); - } -#endif - cache.validArea = QRect(); -// recreateWholeImageCache = true; - } - } + int x0 = v->getXForViewX(rect.x()); + int x1 = v->getXForViewX(rect.x() + rect.width()); + if (x0 < 0) x0 = 0; + if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth(); if (updateViewMagnitudes(v)) { #ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; + cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl; #endif - if (m_normalizeVisibleArea) { - cache.validArea = QRect(); - recreateWholeImageCache = true; + if (m_normalization == NormalizeVisibleArea) { + cache.invalidate(); + } + } + + if (cache.getZoomLevel() != zoomLevel || + cache.getSize() != v->getPaintSize()) { +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer: resizing image cache from " + << cache.getSize().width() << "x" << cache.getSize().height() + << " to " + << v->getPaintSize().width() << "x" << v->getPaintSize().height() + << " and updating zoom level from " << cache.getZoomLevel() + << " to " << zoomLevel + << endl; +#endif + cache.resize(v->getPaintSize()); + cache.setZoomLevel(zoomLevel); + cache.setStartFrame(startFrame); + } + + if (cache.isValid()) { + + if (v->getXForFrame(cache.getStartFrame()) == + v->getXForFrame(startFrame) && + cache.getValidLeft() <= x0 && + cache.getValidRight() >= x1) { + +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer: image cache hit!" << endl; +#endif + + paint.drawImage(rect, cache.getImage(), rect); + + illuminateLocalFeatures(v, paint); + return; + + } else { + + // cache doesn't begin at the right frame or doesn't + // contain the complete view, but might be scrollable or + // partially usable + +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer: scrolling the image cache if applicable" << endl; +#endif + + cache.scrollTo(startFrame); + +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer: after scrolling, cache valid from " + << cache.getValidLeft() << " width " << cache.getValidWidth() + << endl; +#endif + } + } + + bool rightToLeft = false; + + if (!cache.isValid()) { + if (!m_synchronous) { + // When rendering the whole thing, start from somewhere near + // the middle so that the region of interest appears first + + //!!! (perhaps we should have some cunning test to avoid + //!!! doing this if past repaints have appeared fast + //!!! enough to do the whole width in one shot) + if (x0 == 0 && x1 == v->getPaintWidth()) { + x0 = int(x1 * 0.3); + } } } else { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; -#endif + // When rendering only a part of the cache, we need to make + // sure that the part we're rendering is adjacent to (or + // overlapping) a valid area of cache, if we have one. The + // alternative is to ditch the valid area of cache and render + // only the requested area, but that's risky because this can + // happen when just waving the pointer over a small part of + // the view -- if we lose the partly-built cache every time + // the user does that, we'll never finish building it. + int left = x0; + int width = x1 - x0; + bool isLeftOfValidArea = false; + cache.adjustToTouchValidArea(left, width, isLeftOfValidArea); + x0 = left; + x1 = x0 + width; + + // That call also told us whether we should be painting + // sub-regions of our target region in right-to-left order in + // order to ensure contiguity + rightToLeft = isLeftOfValidArea; } - - if (recreateWholeImageCache) { - x0 = 0; - x1 = v->width(); - } - - struct timeval tv; - (void)gettimeofday(&tv, 0); - RealTime mainPaintStart = RealTime::fromTimeval(tv); - - int paintBlockWidth = m_lastPaintBlockWidth; - - if (m_synchronous) { - if (paintBlockWidth < x1 - x0) { - // always paint full width - paintBlockWidth = x1 - x0; - } - } else { - if (paintBlockWidth == 0) { - paintBlockWidth = (300000 / zoomLevel); - } else { - RealTime lastTime = m_lastPaintTime; - while (lastTime > RealTime::fromMilliseconds(200) && - paintBlockWidth > 50) { - paintBlockWidth /= 2; - lastTime = lastTime / 2; - } - while (lastTime < RealTime::fromMilliseconds(90) && - paintBlockWidth < 1500) { - paintBlockWidth *= 2; - lastTime = lastTime * 2; - } - } - - if (paintBlockWidth < 20) paintBlockWidth = 20; - } - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl; -#endif - + // We always paint the full height when refreshing the cache. // Smaller heights can be used when painting direct from cache // (further up in this function), but we want to ensure the cache // is coherent without having to worry about vertical matching of // required and valid areas as well as horizontal. - - int h = v->height(); - - if (cache.validArea.width() > 0) { - - // If part of the cache is known to be valid, select a strip - // immediately to left or right of the valid part - - //!!! this really needs to be coordinated with the selection - //!!! of m_drawBuffer boundaries in the bufferBinResolution - //!!! case below - - int vx0 = 0, vx1 = 0; - vx0 = cache.validArea.x(); - vx1 = cache.validArea.x() + cache.validArea.width(); - + int h = v->getPaintHeight(); + + int repaintWidth = x1 - x0; + #ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl; -#endif - if (x0 < vx0) { - if (x0 + paintBlockWidth < vx0) { - x0 = vx0 - paintBlockWidth; - } - x1 = vx0; - } else if (x0 >= vx1) { - x0 = vx1; - if (x1 > x0 + paintBlockWidth) { - x1 = x0 + paintBlockWidth; - } - } else { - // x0 is within the valid area - if (x1 > vx1) { - x0 = vx1; - if (x0 + paintBlockWidth < x1) { - x1 = x0 + paintBlockWidth; - } - } else { - x1 = x0; // it's all valid, paint nothing - } - } - - cache.validArea = QRect - (std::min(vx0, x0), cache.validArea.y(), - std::max(vx1 - std::min(vx0, x0), - x1 - std::min(vx0, x0)), - cache.validArea.height()); - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Valid area becomes " << cache.validArea.x() - << ", " << cache.validArea.y() << ", " - << cache.validArea.width() << "x" - << cache.validArea.height() << endl; + cerr << "SpectrogramLayer: x0 " << x0 << ", x1 " << x1 + << ", repaintWidth " << repaintWidth << ", h " << h + << ", rightToLeft " << rightToLeft << endl; #endif - - } else { - if (x1 > x0 + paintBlockWidth) { - int sfx = x1; - if (startFrame < 0) sfx = v->getXForFrame(0); - if (sfx >= x0 && sfx + paintBlockWidth <= x1) { - x0 = sfx; - x1 = x0 + paintBlockWidth; - } else { - int mid = (x1 + x0) / 2; - x0 = mid - paintBlockWidth/2; - x1 = x0 + paintBlockWidth; - } - } -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0) - << "x" << h << endl; -#endif - cache.validArea = QRect(x0, 0, x1 - x0, h); - } - -/* - if (xPixelRatio != 1.f) { - x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001); - x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001); - } -*/ - int w = x1 - x0; - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl; -#endif - - int sr = m_model->getSampleRate(); + + sv_samplerate_t sr = m_model->getSampleRate(); // Set minFreq and maxFreq to the frequency extents of the possibly // zero-padded visible bin range, and displayMinFreq and displayMaxFreq @@ -2148,13 +1805,13 @@ // Note fftSize is the actual zero-padded fft size, m_fftSize the // nominal fft size. - size_t maxbin = m_fftSize / 2; + int maxbin = m_fftSize / 2; if (m_maxFrequency > 0) { maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001); if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; } - size_t minbin = 1; + int minbin = 1; if (m_minFrequency > 0) { minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001); // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl; @@ -2166,11 +1823,11 @@ minbin = minbin * zpl; maxbin = (maxbin + 1) * zpl - 1; - float minFreq = (float(minbin) * sr) / fftSize; - float maxFreq = (float(maxbin) * sr) / fftSize; - - float displayMinFreq = minFreq; - float displayMaxFreq = maxFreq; + double minFreq = (double(minbin) * sr) / fftSize; + double maxFreq = (double(maxbin) * sr) / fftSize; + + double displayMinFreq = minFreq; + double displayMaxFreq = maxFreq; if (fftSize != m_fftSize) { displayMinFreq = getEffectiveMinFrequency(); @@ -2182,34 +1839,17 @@ int increment = getWindowIncrement(); bool logarithmic = (m_frequencyScale == LogFrequencyScale); -/* - float yforbin[maxbin - minbin + 1]; - - for (size_t q = minbin; q <= maxbin; ++q) { - float f0 = (float(q) * sr) / fftSize; - yforbin[q - minbin] = - v->getYForFrequency(f0, displayMinFreq, displayMaxFreq, - logarithmic); + + MagnitudeRange overallMag = m_viewMags[v->getId()]; + bool overallMagChanged = false; + +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer: " << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl; +#endif + + if (repaintWidth == 0) { + SVDEBUG << "*** NOTE: repaintWidth == 0" << endl; } -*/ - MagnitudeRange overallMag = m_viewMags[v]; - bool overallMagChanged = false; - - bool fftSuspended = false; - -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl; -#endif - - bool runOutOfData = false; - - if (w == 0) { - SVDEBUG << "*** NOTE: w == 0" << endl; - } - -#ifdef DEBUG_SPECTROGRAM_REPAINT - size_t pixels = 0; -#endif Profiler outerprof("SpectrogramLayer::paint: all cols"); @@ -2226,28 +1866,34 @@ // such boundaries at either side of the draw buffer -- one which // we draw up to, and one which we subsequently crop at. - bool bufferBinResolution = false; - if (increment > zoomLevel) bufferBinResolution = true; - - long leftBoundaryFrame = -1, leftCropFrame = -1; - long rightBoundaryFrame = -1, rightCropFrame = -1; + bool bufferIsBinResolution = false; + if (increment > zoomLevel) bufferIsBinResolution = true; + + sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1; + sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1; int bufwid; - if (bufferBinResolution) { + if (bufferIsBinResolution) { for (int x = x0; ; --x) { - long f = v->getFrameForX(x); + sv_frame_t f = v->getFrameForX(x); if ((f / increment) * increment == f) { if (leftCropFrame == -1) leftCropFrame = f; - else if (x < x0 - 2) { leftBoundaryFrame = f; break; } + else if (x < x0 - 2) { + leftBoundaryFrame = f; + break; + } } } - for (int x = x0 + w; ; ++x) { - long f = v->getFrameForX(x); + for (int x = x0 + repaintWidth; ; ++x) { + sv_frame_t f = v->getFrameForX(x); if ((f / increment) * increment == f) { if (rightCropFrame == -1) rightCropFrame = f; - else if (x > x0 + w + 2) { rightBoundaryFrame = f; break; } + else if (x > x0 + repaintWidth + 2) { + rightBoundaryFrame = f; + break; + } } } #ifdef DEBUG_SPECTROGRAM_REPAINT @@ -2255,107 +1901,135 @@ cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl; #endif - bufwid = (rightBoundaryFrame - leftBoundaryFrame) / increment; + bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment); } else { - bufwid = w; + bufwid = repaintWidth; } -#ifdef __GNUC__ - int binforx[bufwid]; - float binfory[h]; -#else - int *binforx = (int *)alloca(bufwid * sizeof(int)); - float *binfory = (float *)alloca(h * sizeof(float)); -#endif - + vector binforx(bufwid); + vector binfory(h); + bool usePeaksCache = false; - if (bufferBinResolution) { + if (bufferIsBinResolution) { for (int x = 0; x < bufwid; ++x) { - binforx[x] = (leftBoundaryFrame / increment) + x; -// cerr << "binforx[" << x << "] = " << binforx[x] << endl; + binforx[x] = int(leftBoundaryFrame / increment) + x; } m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); } else { for (int x = 0; x < bufwid; ++x) { - float s0 = 0, s1 = 0; + double s0 = 0, s1 = 0; if (getXBinRange(v, x + x0, s0, s1)) { binforx[x] = int(s0 + 0.0001); } else { binforx[x] = -1; //??? } } - if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) { + if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) { m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); } usePeaksCache = (increment * 8) < zoomLevel; if (m_colourScale == PhaseColourScale) usePeaksCache = false; } -// No longer exists in Qt5: m_drawBuffer.setNumColors(256); for (int pixel = 0; pixel < 256; ++pixel) { - m_drawBuffer.setColor(pixel, m_palette.getColour(pixel).rgb()); + m_drawBuffer.setColor((unsigned char)pixel, + m_palette.getColour((unsigned char)pixel).rgb()); } m_drawBuffer.fill(0); - + int attainedBufwid = bufwid; + + double softTimeLimit; + + if (m_synchronous) { + + // must paint the whole thing for synchronous mode, so give + // "no timeout" + softTimeLimit = 0.0; + + } else if (bufferIsBinResolution) { + + // calculating boundaries later will be too fiddly for partial + // paints, and painting should be fast anyway when this is the + // case because it means we're well zoomed in + softTimeLimit = 0.0; + + } else { + + // neither limitation applies, so use a short soft limit + + if (m_binDisplay == PeakFrequencies) { + softTimeLimit = 0.15; + } else { + softTimeLimit = 0.1; + } + } + if (m_binDisplay != PeakFrequencies) { for (int y = 0; y < h; ++y) { - float q0 = 0, q1 = 0; + double q0 = 0, q1 = 0; if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) { binfory[y] = -1; } else { binfory[y] = q0; -// cerr << "binfory[" << y << "] = " << binfory[y] << endl; } } - paintDrawBuffer(v, bufwid, h, binforx, binfory, usePeaksCache, - overallMag, overallMagChanged); + attainedBufwid = + paintDrawBuffer(v, bufwid, h, binforx, binfory, + usePeaksCache, + overallMag, overallMagChanged, + rightToLeft, + softTimeLimit); } else { - paintDrawBufferPeakFrequencies(v, bufwid, h, binforx, - minbin, maxbin, - displayMinFreq, displayMaxFreq, - logarithmic, - overallMag, overallMagChanged); + attainedBufwid = + paintDrawBufferPeakFrequencies(v, bufwid, h, binforx, + minbin, maxbin, + displayMinFreq, displayMaxFreq, + logarithmic, + overallMag, overallMagChanged, + rightToLeft, + softTimeLimit); } -/* - for (int x = 0; x < w / xPixelRatio; ++x) { - - Profiler innerprof("SpectrogramLayer::paint: 1 pixel column"); - - runOutOfData = !paintColumnValues(v, fft, x0, x, - minbin, maxbin, - displayMinFreq, displayMaxFreq, - xPixelRatio, - h, yforbin); - - if (runOutOfData) { + int failedToRepaint = bufwid - attainedBufwid; + + int paintedLeft = x0; + int paintedWidth = x1 - x0; + + if (failedToRepaint > 0) { + #ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Run out of data -- dropping out of loop" << endl; + cerr << "SpectrogramLayer::paint(): Failed to repaint " << failedToRepaint << " of " << bufwid + << " columns in time (so managed to repaint " << bufwid - failedToRepaint << ")" << endl; #endif - break; + + if (rightToLeft) { + paintedLeft += failedToRepaint; } + + paintedWidth -= failedToRepaint; + + if (paintedWidth < 0) { + paintedWidth = 0; + } + + } else if (failedToRepaint < 0) { + cerr << "WARNING: failedToRepaint < 0 (= " << failedToRepaint << ")" + << endl; + failedToRepaint = 0; } -*/ + + if (overallMagChanged) { + m_viewMags[v->getId()] = overallMag; #ifdef DEBUG_SPECTROGRAM_REPAINT -// cerr << pixels << " pixels drawn" << endl; -#endif - - if (overallMagChanged) { - m_viewMags[v] = overallMag; -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl; -#endif - } else { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; + cerr << "SpectrogramLayer: Overall mag is now [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "] - will be updating" << endl; #endif } @@ -2363,156 +2037,180 @@ Profiler profiler2("SpectrogramLayer::paint: draw image"); - if (recreateWholeImageCache) { + if (paintedWidth > 0) { + #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "Recreating image cache: width = " << v->width() - << ", height = " << h << endl; -#endif - cache.image = QImage(v->width(), h, QImage::Format_ARGB32_Premultiplied); - } - - if (w > 0) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "Painting " << w << "x" << h - << " from draw buffer at " << 0 << "," << 0 - << " to " << w << "x" << h << " on cache at " + cerr << "SpectrogramLayer: Copying " << paintedWidth << "x" << h + << " from draw buffer at " << paintedLeft - x0 << "," << 0 + << " to " << paintedWidth << "x" << h << " on cache at " << x0 << "," << 0 << endl; #endif - QPainter cachePainter(&cache.image); - - if (bufferBinResolution) { + if (bufferIsBinResolution) { + int scaledLeft = v->getXForFrame(leftBoundaryFrame); int scaledRight = v->getXForFrame(rightBoundaryFrame); + #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "Rescaling image from " << bufwid + cerr << "SpectrogramLayer: Rescaling image from " << bufwid << "x" << h << " to " << scaledRight-scaledLeft << "x" << h << endl; #endif + Preferences::SpectrogramXSmoothing xsmoothing = Preferences::getInstance()->getSpectrogramXSmoothing(); -// SVDEBUG << "xsmoothing == " << xsmoothing << endl; + QImage scaled = m_drawBuffer.scaled (scaledRight - scaledLeft, h, Qt::IgnoreAspectRatio, ((xsmoothing == Preferences::SpectrogramXInterpolated) ? Qt::SmoothTransformation : Qt::FastTransformation)); + int scaledLeftCrop = v->getXForFrame(leftCropFrame); int scaledRightCrop = v->getXForFrame(rightCropFrame); + #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to " + cerr << "SpectrogramLayer: Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to " << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl; #endif - cachePainter.drawImage - (QRect(scaledLeftCrop, 0, - scaledRightCrop - scaledLeftCrop, h), - scaled, - QRect(scaledLeftCrop - scaledLeft, 0, - scaledRightCrop - scaledLeftCrop, h)); + + int targetLeft = scaledLeftCrop; + if (targetLeft < 0) { + targetLeft = 0; + } + + int targetWidth = scaledRightCrop - targetLeft; + if (targetLeft + targetWidth > cache.getSize().width()) { + targetWidth = cache.getSize().width() - targetLeft; + } + + int sourceLeft = targetLeft - scaledLeft; + if (sourceLeft < 0) { + sourceLeft = 0; + } + + int sourceWidth = targetWidth; + + if (targetWidth > 0) { + cache.drawImage + (targetLeft, + targetWidth, + scaled, + sourceLeft, + sourceWidth); + } + } else { - cachePainter.drawImage(QRect(x0, 0, w, h), - m_drawBuffer, - QRect(0, 0, w, h)); + + cache.drawImage(paintedLeft, paintedWidth, + m_drawBuffer, + paintedLeft - x0, paintedWidth); } - - cachePainter.end(); } - QRect pr = rect & cache.validArea; - #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "Painting " << pr.width() << "x" << pr.height() + cerr << "SpectrogramLayer: Cache valid area now from " << cache.getValidLeft() + << " width " << cache.getValidWidth() << ", height " + << cache.getSize().height() << endl; +#endif + + QRect pr = rect & cache.getValidArea(); + +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer: Copying " << pr.width() << "x" << pr.height() << " from cache at " << pr.x() << "," << pr.y() << " to window" << endl; #endif - paint.drawImage(pr.x(), pr.y(), cache.image, + paint.drawImage(pr.x(), pr.y(), cache.getImage(), pr.x(), pr.y(), pr.width(), pr.height()); - //!!! -// paint.drawImage(v->rect(), cache.image, -// QRect(QPoint(0, 0), cache.image.size())); - - cache.startFrame = startFrame; - cache.zoomLevel = zoomLevel; if (!m_synchronous) { - if (!m_normalizeVisibleArea || !overallMagChanged) { - - if (cache.validArea.x() > 0) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::paint() updating left (0, " - << cache.validArea.x() << ")" << endl; -#endif - v->update(0, 0, cache.validArea.x(), h); + if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) { + + QRect areaLeft(0, 0, cache.getValidLeft(), h); + QRect areaRight(cache.getValidRight(), 0, + cache.getSize().width() - cache.getValidRight(), h); + + bool haveSpaceLeft = (areaLeft.width() > 0); + bool haveSpaceRight = (areaRight.width() > 0); + + bool updateLeft = haveSpaceLeft; + bool updateRight = haveSpaceRight; + + if (updateLeft && updateRight) { + if (rightToLeft) { + // we just did something adjoining the cache on + // its left side, so now do something on its right + updateLeft = false; + } else { + updateRight = false; + } } - if (cache.validArea.x() + cache.validArea.width() < - cache.image.width()) { + if (updateLeft) { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::paint() updating right (" - << cache.validArea.x() + cache.validArea.width() - << ", " - << cache.image.width() - (cache.validArea.x() + - cache.validArea.width()) - << ")" << endl; + cerr << "SpectrogramLayer::paint() updating left (" + << areaLeft.x() << ", " + << areaLeft.width() << ")" << endl; #endif - v->update(cache.validArea.x() + cache.validArea.width(), - 0, - cache.image.width() - (cache.validArea.x() + - cache.validArea.width()), - h); + v->updatePaintRect(areaLeft); } + + if (updateRight) { +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer::paint() updating right (" + << areaRight.x() << ", " + << areaRight.width() << ")" << endl; +#endif + v->updatePaintRect(areaRight); + } + } else { // overallMagChanged cerr << "\noverallMagChanged - updating all\n" << endl; - cache.validArea = QRect(); - v->update(); + cache.invalidate(); + v->updatePaintRect(v->getPaintRect()); } } illuminateLocalFeatures(v, paint); #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::paint() returning" << endl; + cerr << "SpectrogramLayer::paint() returning" << endl; #endif - - if (!m_synchronous) { - m_lastPaintBlockWidth = paintBlockWidth; - (void)gettimeofday(&tv, 0); - m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart; - } - -//!!! if (fftSuspended) fft->resume(); } -bool -SpectrogramLayer::paintDrawBufferPeakFrequencies(View *v, +int +SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, int w, int h, - int *binforx, + const vector &binforx, int minbin, int maxbin, - float displayMinFreq, - float displayMaxFreq, + double displayMinFreq, + double displayMaxFreq, bool logarithmic, MagnitudeRange &overallMag, - bool &overallMagChanged) const + bool &overallMagChanged, + bool rightToLeft, + double softTimeLimit) const { Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies"); #ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; + cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; #endif if (minbin < 0) minbin = 0; if (maxbin < 0) maxbin = minbin+1; FFTModel *fft = getFFTModel(v); - if (!fft) return false; + if (!fft) return 0; FFTModel::PeakSet peakfreqs; - int px = -1, psx = -1; + int psx = -1; #ifdef __GNUC__ float values[maxbin - minbin + 1]; @@ -2520,12 +2218,30 @@ float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float)); #endif - for (int x = 0; x < w; ++x) { + int minColumns = 4; + bool haveTimeLimits = (softTimeLimit > 0.0); + double hardTimeLimit = softTimeLimit * 2.0; + bool overridingSoftLimit = false; + auto startTime = chrono::steady_clock::now(); + + int start = 0; + int finish = w; + int step = 1; + + if (rightToLeft) { + start = w-1; + finish = -1; + step = -1; + } + + int columnCount = 0; + + for (int x = start; x != finish; x += step) { + + ++columnCount; if (binforx[x] < 0) continue; - float columnMax = 0.f; - int sx0 = binforx[x]; int sx1 = sx0; if (x+1 < w) sx1 = binforx[x+1]; @@ -2535,18 +2251,8 @@ for (int sx = sx0; sx < sx1; ++sx) { - if (x == px && sx == psx) continue; if (sx < 0 || sx >= int(fft->getWidth())) continue; - if (!m_synchronous) { - if (!fft->isColumnAvailable(sx)) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Met unavailable column at col " << sx << endl; -#endif - return false; - } - } - MagnitudeRange mag; if (sx != psx) { @@ -2554,8 +2260,16 @@ minbin, maxbin - 1); if (m_colourScale == PhaseColourScale) { fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1); - } else if (m_normalizeColumns) { + } else if (m_normalization == NormalizeColumns) { fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); + } else if (m_normalization == NormalizeHybrid) { + float max = fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); + if (max > 0.f) { + for (int i = minbin; i <= maxbin; ++i) { + values[i - minbin] = float(values[i - minbin] * + log10f(max)); + } + } } else { fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); } @@ -2566,22 +2280,22 @@ pi != peakfreqs.end(); ++pi) { int bin = pi->first; - int freq = pi->second; + double freq = pi->second; if (bin < minbin) continue; if (bin > maxbin) break; - float value = values[bin - minbin]; + double value = values[bin - minbin]; if (m_colourScale != PhaseColourScale) { - if (!m_normalizeColumns) { - value /= (m_fftSize/2.f); + if (m_normalization != NormalizeColumns) { + value /= (m_fftSize/2.0); } - mag.sample(value); + mag.sample(float(value)); value *= m_gain; } - float y = v->getYForFrequency + double y = v->getYForFrequency (freq, displayMinFreq, displayMaxFreq, logarithmic); int iy = int(y + 0.5); @@ -2604,28 +2318,58 @@ } } } + + if (haveTimeLimits) { + if (columnCount >= minColumns) { + auto t = chrono::steady_clock::now(); + double diff = chrono::duration(t - startTime).count(); + if (diff > hardTimeLimit) { +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: hard limit " << hardTimeLimit << " sec exceeded after " + << columnCount << " columns with time " << diff << endl; +#endif + return columnCount; + } else if (diff > softTimeLimit && !overridingSoftLimit) { + // If we're more than half way through by the time + // we reach the soft limit, ignore it (though + // still respect the hard limit, above). Otherwise + // respect the soft limit and return now. + if (columnCount > w/2) { + overridingSoftLimit = true; + } else { +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: soft limit " << softTimeLimit << " sec exceeded after " + << columnCount << " columns with time " << diff << endl; +#endif + return columnCount; + } + } + } + } } - return true; + return columnCount; } -bool -SpectrogramLayer::paintDrawBuffer(View *v, +int +SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v, int w, int h, - int *binforx, - float *binfory, + const vector &binforx, + const vector &binfory, bool usePeaksCache, MagnitudeRange &overallMag, - bool &overallMagChanged) const + bool &overallMagChanged, + bool rightToLeft, + double softTimeLimit) const { Profiler profiler("SpectrogramLayer::paintDrawBuffer"); int minbin = int(binfory[0] + 0.0001); - int maxbin = binfory[h-1]; + int maxbin = int(binfory[h-1]); #ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; + cerr << "SpectrogramLayer::paintDrawBuffer: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; #endif if (minbin < 0) minbin = 0; if (maxbin < 0) maxbin = minbin+1; @@ -2634,7 +2378,7 @@ FFTModel *fft = 0; int divisor = 1; #ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl; + cerr << "SpectrogramLayer::paintDrawBuffer: Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl; #endif if (usePeaksCache) { //!!! sourceModel = getPeakCache(v); @@ -2645,7 +2389,7 @@ sourceModel = fft = getFFTModel(v); } - if (!sourceModel) return false; + if (!sourceModel) return 0; bool interpolate = false; Preferences::SpectrogramSmoothing smoothing = @@ -2671,7 +2415,27 @@ const float *values = autoarray; DenseThreeDimensionalModel::Column c; - for (int x = 0; x < w; ++x) { + int minColumns = 4; + bool haveTimeLimits = (softTimeLimit > 0.0); + double hardTimeLimit = softTimeLimit * 2.0; + bool overridingSoftLimit = false; + auto startTime = chrono::steady_clock::now(); + + int start = 0; + int finish = w; + int step = 1; + + if (rightToLeft) { + start = w-1; + finish = -1; + step = -1; + } + + int columnCount = 0; + + for (int x = start; x != finish; x += step) { + + ++columnCount; if (binforx[x] < 0) continue; @@ -2695,85 +2459,85 @@ if (sx < 0 || sx >= int(sourceModel->getWidth())) continue; - if (!m_synchronous) { - if (!sourceModel->isColumnAvailable(sx)) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - cerr << "Met unavailable column at col " << sx << endl; -#endif - return false; - } - } - MagnitudeRange mag; if (sx != psx) { if (fft) { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "Retrieving column " << sx << " from fft directly" << endl; +// cerr << "Retrieving column " << sx << " from fft directly" << endl; #endif if (m_colourScale == PhaseColourScale) { fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1); - } else if (m_normalizeColumns) { + } else if (m_normalization == NormalizeColumns) { fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); + } else if (m_normalization == NormalizeHybrid) { + float max = fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); + float scale = log10f(max + 1.f); +// cout << "sx = " << sx << ", max = " << max << ", log10(max) = " << log10(max) << ", scale = " << scale << endl; + for (int i = minbin; i <= maxbin; ++i) { + autoarray[i - minbin] *= scale; + } } else { fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); } } else { #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "Retrieving column " << sx << " from peaks cache" << endl; +// cerr << "Retrieving column " << sx << " from peaks cache" << endl; #endif c = sourceModel->getColumn(sx); - if (m_normalizeColumns) { + if (m_normalization == NormalizeColumns || + m_normalization == NormalizeHybrid) { for (int y = 0; y < h; ++y) { if (c[y] > columnMax) columnMax = c[y]; } } - values = c.constData() + minbin; + values = c.data() + minbin; } psx = sx; } for (int y = 0; y < h; ++y) { - float sy0 = binfory[y]; - float sy1 = sy0 + 1; + double sy0 = binfory[y]; + double sy1 = sy0 + 1; if (y+1 < h) sy1 = binfory[y+1]; - float value = 0.f; - - if (interpolate && fabsf(sy1 - sy0) < 1.f) { - - float centre = (sy0 + sy1) / 2; - float dist = (centre - 0.5) - lrintf(centre - 0.5); + double value = 0.0; + + if (interpolate && fabs(sy1 - sy0) < 1.0) { + + double centre = (sy0 + sy1) / 2; + double dist = (centre - 0.5) - rint(centre - 0.5); int bin = int(centre); int other = (dist < 0 ? (bin-1) : (bin+1)); if (bin < minbin) bin = minbin; if (bin > maxbin) bin = maxbin; if (other < minbin || other > maxbin) other = bin; - float prop = 1.f - fabsf(dist); - - float v0 = values[bin - minbin]; - float v1 = values[other - minbin]; + double prop = 1.0 - fabs(dist); + + double v0 = values[bin - minbin]; + double v1 = values[other - minbin]; if (m_binDisplay == PeakBins) { if (bin == minbin || bin == maxbin || v0 < values[bin-minbin-1] || - v0 < values[bin-minbin+1]) v0 = 0.f; + v0 < values[bin-minbin+1]) v0 = 0.0; if (other == minbin || other == maxbin || v1 < values[other-minbin-1] || - v1 < values[other-minbin+1]) v1 = 0.f; + v1 < values[other-minbin+1]) v1 = 0.0; } - if (v0 == 0.f && v1 == 0.f) continue; - value = prop * v0 + (1.f - prop) * v1; + if (v0 == 0.0 && v1 == 0.0) continue; + value = prop * v0 + (1.0 - prop) * v1; if (m_colourScale != PhaseColourScale) { - if (!m_normalizeColumns) { - value /= (m_fftSize/2.f); + if (m_normalization != NormalizeColumns && + m_normalization != NormalizeHybrid) { + value /= (m_fftSize/2.0); } - mag.sample(value); + mag.sample(float(value)); value *= m_gain; } - peaks[y] = value; + peaks[y] = float(value); } else { @@ -2791,14 +2555,17 @@ } if (m_colourScale != PhaseColourScale) { - if (!m_normalizeColumns) { - value /= (m_fftSize/2.f); + if (m_normalization != NormalizeColumns && + m_normalization != NormalizeHybrid) { + value /= (m_fftSize/2.0); } - mag.sample(value); + mag.sample(float(value)); value *= m_gain; } - if (value > peaks[y]) peaks[y] = value; //!!! not right for phase! + if (value > peaks[y]) { + peaks[y] = float(value); //!!! not right for phase! + } } } } @@ -2820,25 +2587,57 @@ for (int y = 0; y < h; ++y) { - float peak = peaks[y]; + double peak = peaks[y]; if (m_colourScale != PhaseColourScale && - m_normalizeColumns && + (m_normalization == NormalizeColumns || + m_normalization == NormalizeHybrid) && columnMax > 0.f) { peak /= columnMax; + if (m_normalization == NormalizeHybrid) { + peak *= log10(columnMax + 1.f); + } } unsigned char peakpix = getDisplayValue(v, peak); m_drawBuffer.setPixel(x, h-y-1, peakpix); } + + if (haveTimeLimits) { + if (columnCount >= minColumns) { + auto t = chrono::steady_clock::now(); + double diff = chrono::duration(t - startTime).count(); + if (diff > hardTimeLimit) { +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer::paintDrawBuffer: hard limit " << hardTimeLimit << " sec exceeded after " + << columnCount << " columns with time " << diff << endl; +#endif + return columnCount; + } else if (diff > softTimeLimit && !overridingSoftLimit) { + // If we're more than half way through by the time + // we reach the soft limit, ignore it (though + // still respect the hard limit, above). Otherwise + // respect the soft limit and return now. + if (columnCount > w/2) { + overridingSoftLimit = true; + } else { +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "SpectrogramLayer::paintDrawBuffer: soft limit " << softTimeLimit << " sec exceeded after " + << columnCount << " columns with time " << diff << endl; +#endif + return columnCount; + } + } + } + } } - return true; + return columnCount; } void -SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const +SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const { Profiler profiler("SpectrogramLayer::illuminateLocalFeatures"); @@ -2850,8 +2649,8 @@ // cerr << "SpectrogramLayer: illuminateLocalFeatures(" // << localPos.x() << "," << localPos.y() << ")" << endl; - float s0, s1; - float f0, f1; + double s0, s1; + double f0, f1; if (getXBinRange(v, localPos.x(), s0, s1) && getYBinSourceRange(v, localPos.y(), f0, f1)) { @@ -2876,8 +2675,8 @@ } } -float -SpectrogramLayer::getYForFrequency(const View *v, float frequency) const +double +SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const { return v->getYForFrequency(frequency, getEffectiveMinFrequency(), @@ -2885,8 +2684,8 @@ m_frequencyScale == LogFrequencyScale); } -float -SpectrogramLayer::getFrequencyForY(const View *v, int y) const +double +SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const { return v->getFrequencyForY(y, getEffectiveMinFrequency(), @@ -2895,34 +2694,36 @@ } int -SpectrogramLayer::getCompletion(View *v) const +SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const { - if (m_updateTimer == 0) return 100; - if (m_fftModels.find(v) == m_fftModels.end()) return 100; - - size_t completion = m_fftModels[v].first->getCompletion(); + const View *view = v->getView(); + + if (m_fftModels.find(view->getId()) == m_fftModels.end()) return 100; + + int completion = m_fftModels[view->getId()]->getCompletion(); #ifdef DEBUG_SPECTROGRAM_REPAINT - SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl; + cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl; #endif return completion; } QString -SpectrogramLayer::getError(View *v) const +SpectrogramLayer::getError(LayerGeometryProvider *v) const { - if (m_fftModels.find(v) == m_fftModels.end()) return ""; - return m_fftModels[v].first->getError(); + const View *view = v->getView(); + if (m_fftModels.find(view->getId()) == m_fftModels.end()) return ""; + return m_fftModels[view->getId()]->getError(); } bool -SpectrogramLayer::getValueExtents(float &min, float &max, +SpectrogramLayer::getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const { if (!m_model) return false; - int sr = m_model->getSampleRate(); - min = float(sr) / m_fftSize; - max = float(sr) / 2; + sv_samplerate_t sr = m_model->getSampleRate(); + min = double(sr) / m_fftSize; + max = double(sr) / 2; logarithmic = (m_frequencyScale == LogFrequencyScale); unit = "Hz"; @@ -2930,7 +2731,7 @@ } bool -SpectrogramLayer::getDisplayExtents(float &min, float &max) const +SpectrogramLayer::getDisplayExtents(double &min, double &max) const { min = getEffectiveMinFrequency(); max = getEffectiveMaxFrequency(); @@ -2940,17 +2741,17 @@ } bool -SpectrogramLayer::setDisplayExtents(float min, float max) +SpectrogramLayer::setDisplayExtents(double min, double max) { if (!m_model) return false; // SVDEBUG << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << endl; if (min < 0) min = 0; - if (max > m_model->getSampleRate()/2) max = m_model->getSampleRate()/2; + if (max > m_model->getSampleRate()/2.0) max = m_model->getSampleRate()/2.0; - size_t minf = lrintf(min); - size_t maxf = lrintf(max); + int minf = int(lrint(min)); + int maxf = int(lrint(max)); if (m_minFrequency == minf && m_maxFrequency == maxf) return true; @@ -2972,8 +2773,8 @@ } bool -SpectrogramLayer::getYScaleValue(const View *v, int y, - float &value, QString &unit) const +SpectrogramLayer::getYScaleValue(const LayerGeometryProvider *v, int y, + double &value, QString &unit) const { value = getFrequencyForY(v, y); unit = "Hz"; @@ -2981,13 +2782,14 @@ } bool -SpectrogramLayer::snapToFeatureFrame(View *, int &frame, - size_t &resolution, +SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *, + sv_frame_t &frame, + int &resolution, SnapType snap) const { resolution = getWindowIncrement(); - int left = (frame / resolution) * resolution; - int right = left + resolution; + sv_frame_t left = (frame / resolution) * resolution; + sv_frame_t right = left + resolution; switch (snap) { case SnapLeft: frame = left; break; @@ -3003,14 +2805,15 @@ } void -SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e) +SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e) { - ImageCache &cache = m_imageCaches[v]; - - cerr << "cache width: " << cache.image.width() << ", height: " - << cache.image.height() << endl; - - QImage image = cache.image; + const View *view = v->getView(); + ScrollableImageCache &cache = getImageCacheReference(view); + + cerr << "cache width: " << cache.getSize().width() << ", height: " + << cache.getSize().height() << endl; + + QImage image = cache.getImage(); ImageRegionFinder finder; QRect rect = finder.findRegionExtents(&image, e->pos()); @@ -3023,11 +2826,11 @@ } bool -SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint, +SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint, QPoint cursorPos, - std::vector &extents) const + vector &extents) const { - QRect vertical(cursorPos.x() - 12, 0, 12, v->height()); + QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight()); extents.push_back(vertical); QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); @@ -3046,14 +2849,14 @@ extents.push_back(pitch); QRect rt(cursorPos.x(), - v->height() - paint.fontMetrics().height() - 2, + v->getPaintHeight() - paint.fontMetrics().height() - 2, paint.fontMetrics().width("1234.567 s"), paint.fontMetrics().height()); extents.push_back(rt); int w(paint.fontMetrics().width("1234567890") + 2); QRect frame(cursorPos.x() - w - 2, - v->height() - paint.fontMetrics().height() - 2, + v->getPaintHeight() - paint.fontMetrics().height() - 2, w, paint.fontMetrics().height()); extents.push_back(frame); @@ -3062,7 +2865,7 @@ } void -SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint, +SpectrogramLayer::paintCrosshairs(LayerGeometryProvider *v, QPainter &paint, QPoint cursorPos) const { paint.save(); @@ -3077,9 +2880,9 @@ paint.setPen(m_crosshairColour); paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y()); - paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height()); + paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->getPaintHeight()); - float fundamental = getFrequencyForY(v, cursorPos.y()); + double fundamental = getFrequencyForY(v, cursorPos.y()); v->drawVisibleText(paint, sw + 2, @@ -3096,18 +2899,18 @@ View::OutlinedText); } - long frame = v->getFrameForX(cursorPos.x()); + sv_frame_t frame = v->getFrameForX(cursorPos.x()); RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str()); QString frameLabel = QString("%1").arg(frame); v->drawVisibleText(paint, cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2, - v->height() - 2, + v->getPaintHeight() - 2, frameLabel, View::OutlinedText); v->drawVisibleText(paint, cursorPos.x() + 2, - v->height() - 2, + v->getPaintHeight() - 2, rtLabel, View::OutlinedText); @@ -3115,8 +2918,8 @@ while (harmonic < 100) { - float hy = lrintf(getYForFrequency(v, fundamental * harmonic)); - if (hy < 0 || hy > v->height()) break; + int hy = int(lrint(getYForFrequency(v, fundamental * harmonic))); + if (hy < 0 || hy > v->getPaintHeight()) break; int len = 7; @@ -3129,9 +2932,9 @@ } paint.drawLine(cursorPos.x() - len, - int(hy), + hy, cursorPos.x(), - int(hy)); + hy); ++harmonic; } @@ -3140,17 +2943,17 @@ } QString -SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const +SpectrogramLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); int y = pos.y(); if (!m_model || !m_model->isOK()) return ""; - float magMin = 0, magMax = 0; - float phaseMin = 0, phaseMax = 0; - float freqMin = 0, freqMax = 0; - float adjFreqMin = 0, adjFreqMax = 0; + double magMin = 0, magMax = 0; + double phaseMin = 0, phaseMax = 0; + double freqMin = 0, freqMax = 0; + double adjFreqMin = 0, adjFreqMax = 0; QString pitchMin, pitchMax; RealTime rtMin, rtMax; @@ -3222,21 +3025,21 @@ } if (haveValues) { - float dbMin = AudioLevel::multiplier_to_dB(magMin); - float dbMax = AudioLevel::multiplier_to_dB(magMax); + double dbMin = AudioLevel::multiplier_to_dB(magMin); + double dbMax = AudioLevel::multiplier_to_dB(magMax); QString dbMinString; QString dbMaxString; if (dbMin == AudioLevel::DB_FLOOR) { dbMinString = tr("-Inf"); } else { - dbMinString = QString("%1").arg(lrintf(dbMin)); + dbMinString = QString("%1").arg(lrint(dbMin)); } if (dbMax == AudioLevel::DB_FLOOR) { dbMaxString = tr("-Inf"); } else { - dbMaxString = QString("%1").arg(lrintf(dbMax)); + dbMaxString = QString("%1").arg(lrint(dbMax)); } - if (lrintf(dbMin) != lrintf(dbMax)) { + if (lrint(dbMin) != lrint(dbMax)) { text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); } else { text += tr("dB:\t%1").arg(dbMinString); @@ -3262,7 +3065,7 @@ } int -SpectrogramLayer::getVerticalScaleWidth(View *, bool detailed, QPainter &paint) const +SpectrogramLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool detailed, QPainter &paint) const { if (!m_model || !m_model->isOK()) return 0; @@ -3283,7 +3086,7 @@ } void -SpectrogramLayer::paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const +SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) { return; @@ -3298,8 +3101,8 @@ int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0); - size_t bins = m_fftSize / 2; - int sr = m_model->getSampleRate(); + int bins = m_fftSize / 2; + sv_samplerate_t sr = m_model->getSampleRate(); if (m_maxFrequency > 0) { bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); @@ -3325,17 +3128,23 @@ paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); QString top, bottom; - float min = m_viewMags[v].getMin(); - float max = m_viewMags[v].getMax(); - - float dBmin = AudioLevel::multiplier_to_dB(min); - float dBmax = AudioLevel::multiplier_to_dB(max); - + double min = m_viewMags[v->getId()].getMin(); + double max = m_viewMags[v->getId()].getMax(); + + double dBmin = AudioLevel::multiplier_to_dB(min); + double dBmax = AudioLevel::multiplier_to_dB(max); + +#ifdef DEBUG_SPECTROGRAM_REPAINT + cerr << "paintVerticalScale: for view id " << v->getId() + << ": min = " << min << ", max = " << max + << ", dBmin = " << dBmin << ", dBmax = " << dBmax << endl; +#endif + if (dBmax < -60.f) dBmax = -60.f; - else top = QString("%1").arg(lrintf(dBmax)); + else top = QString("%1").arg(lrint(dBmax)); if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f; - bottom = QString("%1").arg(lrintf(dBmin)); + bottom = QString("%1").arg(lrint(dBmin)); //!!! & phase etc @@ -3359,13 +3168,13 @@ for (int i = 0; i < ch; ++i) { - float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1)); + double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1)); int idb = int(dBval); - float value = AudioLevel::dB_to_multiplier(dBval); + double value = AudioLevel::dB_to_multiplier(dBval); int colour = getDisplayValue(v, value * m_gain); - paint.setPen(m_palette.getColour(colour)); + paint.setPen(m_palette.getColour((unsigned char)colour)); int y = textHeight * topLines + 4 + ch - i; @@ -3397,10 +3206,10 @@ int bin = -1; - for (int y = 0; y < v->height(); ++y) { - - float q0, q1; - if (!getYBinRange(v, v->height() - y, q0, q1)) continue; + for (int y = 0; y < v->getPaintHeight(); ++y) { + + double q0, q1; + if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue; int vy; @@ -3411,7 +3220,7 @@ continue; } - int freq = (sr * bin) / m_fftSize; + int freq = int((sr * bin) / m_fftSize); if (py >= 0 && (vy - py) < textHeight - 1) { if (m_frequencyScale == LinearFrequencyScale) { @@ -3425,7 +3234,7 @@ paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); if (h - vy - textHeight >= -2) { - int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw); + int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw); paint.drawText(tx, h - vy + toff, text); } @@ -3447,33 +3256,38 @@ class SpectrogramRangeMapper : public RangeMapper { public: - SpectrogramRangeMapper(int sr, int /* fftsize */) : - m_dist(float(sr) / 2), - m_s2(sqrtf(sqrtf(2))) { } + SpectrogramRangeMapper(sv_samplerate_t sr, int /* fftsize */) : + m_dist(sr / 2), + m_s2(sqrt(sqrt(2))) { } ~SpectrogramRangeMapper() { } - virtual int getPositionForValue(float value) const { - - float dist = m_dist; + virtual int getPositionForValue(double value) const { + + double dist = m_dist; int n = 0; - while (dist > (value + 0.00001) && dist > 0.1f) { + while (dist > (value + 0.00001) && dist > 0.1) { dist /= m_s2; ++n; } return n; } - - virtual float getValueForPosition(int position) const { + + virtual int getPositionForValueUnclamped(double value) const { + // We don't really support this + return getPositionForValue(value); + } + + virtual double getValueForPosition(int position) const { // Vertical zoom step 0 shows the entire range from DC -> // Nyquist frequency. Step 1 shows 2^(1/4) of the range of // step 0, and so on until the visible range is smaller than // the frequency step between bins at the current fft size. - float dist = m_dist; + double dist = m_dist; int n = 0; while (n < position) { @@ -3484,11 +3298,16 @@ return dist; } + virtual double getValueForPositionUnclamped(int position) const { + // We don't really support this + return getValueForPosition(position); + } + virtual QString getUnit() const { return "Hz"; } protected: - float m_dist; - float m_s2; + double m_dist; + double m_s2; }; int @@ -3496,16 +3315,16 @@ { if (!m_model) return 0; - int sr = m_model->getSampleRate(); + sv_samplerate_t sr = m_model->getSampleRate(); SpectrogramRangeMapper mapper(sr, m_fftSize); -// int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001); +// int maxStep = mapper.getPositionForValue((double(sr) / m_fftSize) + 0.001); int maxStep = mapper.getPositionForValue(0); - int minStep = mapper.getPositionForValue(float(sr) / 2); - - size_t initialMax = m_initialMaxFrequency; - if (initialMax == 0) initialMax = sr / 2; + int minStep = mapper.getPositionForValue(double(sr) / 2); + + int initialMax = m_initialMaxFrequency; + if (initialMax == 0) initialMax = int(sr / 2); defaultStep = mapper.getPositionForValue(initialMax) - minStep; @@ -3519,7 +3338,7 @@ { if (!m_model) return 0; - float dmin, dmax; + double dmin, dmax; getDisplayExtents(dmin, dmax); SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize); @@ -3533,16 +3352,16 @@ { if (!m_model) return; - float dmin = m_minFrequency, dmax = m_maxFrequency; + double dmin = m_minFrequency, dmax = m_maxFrequency; // getDisplayExtents(dmin, dmax); // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl; - int sr = m_model->getSampleRate(); + sv_samplerate_t sr = m_model->getSampleRate(); SpectrogramRangeMapper mapper(sr, m_fftSize); - float newdist = mapper.getValueForPosition(step); - - float newmin, newmax; + double newdist = mapper.getValueForPosition(step); + + double newmin, newmax; if (m_frequencyScale == LogFrequencyScale) { @@ -3567,20 +3386,20 @@ // = dmin.dmax // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2 - newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2; + newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2; newmin = newmax - newdist; // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl; } else { - float dmid = (dmax + dmin) / 2; + double dmid = (dmax + dmin) / 2; newmin = dmid - newdist / 2; newmax = dmid + newdist / 2; } - float mmin, mmax; + double mmin, mmax; mmin = 0; - mmax = float(sr) / 2; + mmax = double(sr) / 2; if (newmin < mmin) { newmax += (mmin - newmin); @@ -3592,8 +3411,8 @@ // SVDEBUG << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl; - setMinFrequency(lrintf(newmin)); - setMaxFrequency(lrintf(newmax)); + setMinFrequency(int(lrint(newmin))); + setMaxFrequency(int(lrint(newmax))); } RangeMapper * @@ -3604,13 +3423,13 @@ } void -SpectrogramLayer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const +SpectrogramLayer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const { int y0 = 0; - if (r.startY > 0.0) y0 = getYForFrequency(v, r.startY); + if (r.startY > 0.0) y0 = int(getYForFrequency(v, r.startY)); int y1 = y0; - if (r.endY > 0.0) y1 = getYForFrequency(v, r.endY); + if (r.endY > 0.0) y1 = int(getYForFrequency(v, r.endY)); // SVDEBUG << "SpectrogramLayer::updateMeasureRectYCoords: start " << r.startY << " -> " << y0 << ", end " << r.endY << " -> " << y1 << endl; @@ -3618,7 +3437,7 @@ } void -SpectrogramLayer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const +SpectrogramLayer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const { if (start) { r.startY = getFrequencyForY(v, y); @@ -3653,19 +3472,38 @@ "colourScheme=\"%4\" " "colourRotation=\"%5\" " "frequencyScale=\"%6\" " - "binDisplay=\"%7\" " - "normalizeColumns=\"%8\" " - "normalizeVisibleArea=\"%9\"") + "binDisplay=\"%7\" ") .arg(m_minFrequency) .arg(m_maxFrequency) .arg(m_colourScale) .arg(m_colourMap) .arg(m_colourRotation) .arg(m_frequencyScale) - .arg(m_binDisplay) - .arg(m_normalizeColumns ? "true" : "false") - .arg(m_normalizeVisibleArea ? "true" : "false"); - + .arg(m_binDisplay); + + // New-style normalization attributes, allowing for more types of + // normalization in future: write out the column normalization + // type separately, and then whether we are normalizing visible + // area as well afterwards + + s += QString("columnNormalization=\"%1\" ") + .arg(m_normalization == NormalizeColumns ? "peak" : + m_normalization == NormalizeHybrid ? "hybrid" : "none"); + + // Old-style normalization attribute. We *don't* write out + // normalizeHybrid here because the only release that would accept + // it (Tony v1.0) has a totally different scale factor for + // it. We'll just have to accept that session files from Tony + // v2.0+ will look odd in Tony v1.0 + + s += QString("normalizeColumns=\"%1\" ") + .arg(m_normalization == NormalizeColumns ? "true" : "false"); + + // And this applies to both old- and new-style attributes + + s += QString("normalizeVisibleArea=\"%1\" ") + .arg(m_normalization == NormalizeVisibleArea ? "true" : "false"); + Layer::toXml(stream, indent, extraAttributes + " " + s); } @@ -3677,13 +3515,13 @@ int channel = attributes.value("channel").toInt(&ok); if (ok) setChannel(channel); - size_t windowSize = attributes.value("windowSize").toUInt(&ok); + int windowSize = attributes.value("windowSize").toUInt(&ok); if (ok) setWindowSize(windowSize); - size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); + int windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); if (ok) setWindowHopLevel(windowHopLevel); else { - size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok); + int windowOverlap = attributes.value("windowOverlap").toUInt(&ok); // a percentage value if (ok) { if (windowOverlap == 0) setWindowHopLevel(0); @@ -3700,13 +3538,13 @@ float threshold = attributes.value("threshold").toFloat(&ok); if (ok) setThreshold(threshold); - size_t minFrequency = attributes.value("minFrequency").toUInt(&ok); + int minFrequency = attributes.value("minFrequency").toUInt(&ok); if (ok) { SVDEBUG << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl; setMinFrequency(minFrequency); } - size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok); + int maxFrequency = attributes.value("maxFrequency").toUInt(&ok); if (ok) { SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl; setMaxFrequency(maxFrequency); @@ -3730,12 +3568,54 @@ attributes.value("binDisplay").toInt(&ok); if (ok) setBinDisplay(binDisplay); - bool normalizeColumns = - (attributes.value("normalizeColumns").trimmed() == "true"); - setNormalizeColumns(normalizeColumns); + bool haveNewStyleNormalization = false; + + QString columnNormalization = attributes.value("columnNormalization"); + + if (columnNormalization != "") { + + haveNewStyleNormalization = true; + + if (columnNormalization == "peak") { + setNormalization(NormalizeColumns); + } else if (columnNormalization == "hybrid") { + setNormalization(NormalizeHybrid); + } else if (columnNormalization == "none") { + // do nothing + } else { + cerr << "NOTE: Unknown or unsupported columnNormalization attribute \"" + << columnNormalization << "\"" << endl; + } + } + + if (!haveNewStyleNormalization) { + + bool normalizeColumns = + (attributes.value("normalizeColumns").trimmed() == "true"); + if (normalizeColumns) { + setNormalization(NormalizeColumns); + } + + bool normalizeHybrid = + (attributes.value("normalizeHybrid").trimmed() == "true"); + if (normalizeHybrid) { + setNormalization(NormalizeHybrid); + } + } bool normalizeVisibleArea = (attributes.value("normalizeVisibleArea").trimmed() == "true"); - setNormalizeVisibleArea(normalizeVisibleArea); + if (normalizeVisibleArea) { + setNormalization(NormalizeVisibleArea); + } + + if (!haveNewStyleNormalization && m_normalization == NormalizeHybrid) { + // Tony v1.0 is (and hopefully will remain!) the only released + // SV-a-like to use old-style attributes when saving sessions + // that ask for hybrid normalization. It saves them with the + // wrong gain factor, so hack in a fix for that here -- this + // gives us backward but not forward compatibility. + setGain(m_gain / float(m_fftSize / 2)); + } } diff -r 282f4be8f058 -r c02c51ae5238 layer/SpectrogramLayer.h --- a/layer/SpectrogramLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/SpectrogramLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _SPECTROGRAM_LAYER_H_ -#define _SPECTROGRAM_LAYER_H_ +#ifndef SPECTROGRAM_LAYER_H +#define SPECTROGRAM_LAYER_H #include "SliceableLayer.h" #include "base/Window.h" @@ -25,6 +25,8 @@ #include "data/model/DenseTimeValueModel.h" #include "data/model/FFTModel.h" +#include "ScrollableImageCache.h" + #include #include #include @@ -57,23 +59,23 @@ virtual const ZoomConstraint *getZoomConstraint() const { return this; } virtual const Model *getModel() const { return m_model; } - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; virtual void setSynchronousPainting(bool synchronous); - virtual int getVerticalScaleWidth(View *v, bool detailed, QPainter &) const; - virtual void paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const; + virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool detailed, QPainter &) const; + virtual void paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const; - virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos, + virtual bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint cursorPos, std::vector &extents) const; - virtual void paintCrosshairs(View *, QPainter &, QPoint) const; + virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; - virtual bool snapToFeatureFrame(View *v, int &frame, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual void measureDoubleClick(View *, QMouseEvent *); + virtual void measureDoubleClick(LayerGeometryProvider *, QMouseEvent *); virtual bool hasLightBackground() const; @@ -88,6 +90,8 @@ int *min, int *max, int *deflt) const; virtual QString getPropertyValueLabel(const PropertyName &, int value) const; + virtual QString getPropertyValueIconName(const PropertyName &, + int value) const; virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; virtual void setProperty(const PropertyName &, int value); @@ -99,17 +103,17 @@ void setChannel(int); int getChannel() const; - void setWindowSize(size_t); - size_t getWindowSize() const; + void setWindowSize(int); + int getWindowSize() const; - void setWindowHopLevel(size_t level); - size_t getWindowHopLevel() const; + void setWindowHopLevel(int level); + int getWindowHopLevel() const; void setWindowType(WindowType type); WindowType getWindowType() const; - void setZeroPadLevel(size_t level); - size_t getZeroPadLevel() const; + void setZeroPadLevel(int level); + int getZeroPadLevel() const; /** * Set the gain multiplier for sample values in this view. @@ -127,11 +131,11 @@ void setThreshold(float threshold); float getThreshold() const; - void setMinFrequency(size_t); - size_t getMinFrequency() const; + void setMinFrequency(int); + int getMinFrequency() const; - void setMaxFrequency(size_t); // 0 -> no maximum - size_t getMaxFrequency() const; + void setMaxFrequency(int); // 0 -> no maximum + int getMaxFrequency() const; enum ColourScale { LinearColourScale, @@ -171,12 +175,23 @@ void setBinDisplay(BinDisplay); BinDisplay getBinDisplay() const; - void setNormalizeColumns(bool n); - bool getNormalizeColumns() const; + enum Normalization { + NoNormalization, + NormalizeColumns, + NormalizeVisibleArea, + NormalizeHybrid + }; - void setNormalizeVisibleArea(bool n); - bool getNormalizeVisibleArea() const; + /** + * Specify the normalization mode for bin values. + */ + void setNormalization(Normalization); + Normalization getNormalization() const; + /** + * Specify the colour map. See ColourMapper for the colour map + * values. + */ void setColourMap(int map); int getColourMap() const; @@ -196,29 +211,29 @@ return ColourHasMeaningfulValue; } - float getYForFrequency(const View *v, float frequency) const; - float getFrequencyForY(const View *v, int y) const; + double getYForFrequency(const LayerGeometryProvider *v, double frequency) const; + double getFrequencyForY(const LayerGeometryProvider *v, int y) const; - virtual int getCompletion(View *v) const; - virtual QString getError(View *v) const; + virtual int getCompletion(LayerGeometryProvider *v) const; + virtual QString getError(LayerGeometryProvider *v) const; - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const; - virtual bool getDisplayExtents(float &min, float &max) const; + virtual bool getDisplayExtents(double &min, double &max) const; - virtual bool setDisplayExtents(float min, float max); + virtual bool setDisplayExtents(double min, double max); - virtual bool getYScaleValue(const View *, int, float &, QString &) const; + virtual bool getYScaleValue(const LayerGeometryProvider *, int, double &, QString &) const; virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; void setProperties(const QXmlAttributes &attributes); - virtual void setLayerDormant(const View *v, bool dormant); + virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant); - virtual bool isLayerScrollable(const View *) const { return false; } + virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; } virtual int getVerticalZoomSteps(int &defaultStep) const; virtual int getCurrentVerticalZoomStep() const; @@ -229,43 +244,38 @@ protected slots: void cacheInvalid(); - void cacheInvalid(size_t startFrame, size_t endFrame); + void cacheInvalid(sv_frame_t startFrame, sv_frame_t endFrame); void preferenceChanged(PropertyContainer::PropertyName name); - void fillTimerTimedOut(); - protected: const DenseTimeValueModel *m_model; // I do not own this int m_channel; - size_t m_windowSize; + int m_windowSize; WindowType m_windowType; - size_t m_windowHopLevel; - size_t m_zeroPadLevel; - size_t m_fftSize; + int m_windowHopLevel; + int m_zeroPadLevel; + int m_fftSize; float m_gain; float m_initialGain; float m_threshold; float m_initialThreshold; int m_colourRotation; int m_initialRotation; - size_t m_minFrequency; - size_t m_maxFrequency; - size_t m_initialMaxFrequency; + int m_minFrequency; + int m_maxFrequency; + int m_initialMaxFrequency; ColourScale m_colourScale; int m_colourMap; QColor m_crosshairColour; FrequencyScale m_frequencyScale; BinDisplay m_binDisplay; - bool m_normalizeColumns; - bool m_normalizeVisibleArea; + Normalization m_normalization; int m_lastEmittedZoomStep; bool m_synchronous; mutable bool m_haveDetailedScale; - mutable int m_lastPaintBlockWidth; - mutable RealTime m_lastPaintTime; enum { NO_VALUE = 0 }; // colour index for unused pixels @@ -286,22 +296,10 @@ Palette m_palette; - /** - * ImageCache covers the area of the view, at view resolution. - * Not all of it is necessarily valid at once (it is refreshed - * in parts when scrolling, for example). - */ - struct ImageCache - { - QImage image; - QRect validArea; - long startFrame; - size_t zoomLevel; - }; - typedef std::map ViewImageCache; + typedef std::map ViewImageCache; // key is view id void invalidateImageCaches(); - void invalidateImageCaches(size_t startFrame, size_t endFrame); mutable ViewImageCache m_imageCaches; + ScrollableImageCache &getImageCacheReference(const LayerGeometryProvider *) const; /** * When painting, we draw directly onto the draw buffer and then @@ -311,30 +309,19 @@ */ mutable QImage m_drawBuffer; - mutable QTimer *m_updateTimer; - - mutable size_t m_candidateFillStartFrame; bool m_exiting; void initialisePalette(); void rotatePalette(int distance); - unsigned char getDisplayValue(View *v, float input) const; - float getInputForDisplayValue(unsigned char uc) const; + unsigned char getDisplayValue(LayerGeometryProvider *v, double input) const; int getColourScaleWidth(QPainter &) const; - void illuminateLocalFeatures(View *v, QPainter &painter) const; + void illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &painter) const; - float getEffectiveMinFrequency() const; - float getEffectiveMaxFrequency() const; - - struct LayerRange { - long startFrame; - int zoomLevel; - size_t modelStart; - size_t modelEnd; - }; + double getEffectiveMinFrequency() const; + double getEffectiveMaxFrequency() const; // Note that the getYBin... methods return the nominal bin in the // un-smoothed spectrogram. This is not necessarily the same bin @@ -342,33 +329,32 @@ // position, if the spectrogram has oversampling smoothing. Use // getSmoothedYBinRange to obtain that. - bool getXBinRange(View *v, int x, float &windowMin, float &windowMax) const; - bool getYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const; - bool getSmoothedYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const; + bool getXBinRange(LayerGeometryProvider *v, int x, double &windowMin, double &windowMax) const; + bool getYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const; + bool getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const; - bool getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) const; - bool getAdjustedYBinSourceRange(View *v, int x, int y, - float &freqMin, float &freqMax, - float &adjFreqMin, float &adjFreqMax) const; - bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const; - bool getXYBinSourceRange(View *v, int x, int y, float &min, float &max, - float &phaseMin, float &phaseMax) const; + bool getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) const; + bool getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, + double &freqMin, double &freqMax, + double &adjFreqMin, double &adjFreqMax) const; + bool getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &timeMin, RealTime &timeMax) const; + bool getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, double &min, double &max, + double &phaseMin, double &phaseMax) const; - size_t getWindowIncrement() const { + int getWindowIncrement() const { if (m_windowHopLevel == 0) return m_windowSize; else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4; else return m_windowSize / (1 << (m_windowHopLevel - 1)); } - size_t getZeroPadLevel(const View *v) const; - size_t getFFTSize(const View *v) const; - FFTModel *getFFTModel(const View *v) const; - Dense3DModelPeakCache *getPeakCache(const View *v) const; + int getZeroPadLevel(const LayerGeometryProvider *v) const; + int getFFTSize(const LayerGeometryProvider *v) const; + FFTModel *getFFTModel(const LayerGeometryProvider *v) const; + Dense3DModelPeakCache *getPeakCache(const LayerGeometryProvider *v) const; void invalidateFFTModels(); - typedef std::pair FFTFillPair; // model, last fill - typedef std::map ViewFFTMap; - typedef std::map PeakCacheMap; + typedef std::map ViewFFTMap; // key is view id + typedef std::map PeakCacheMap; // key is view id mutable ViewFFTMap m_fftModels; mutable PeakCacheMap m_peakCaches; mutable Model *m_sliceableModel; @@ -379,20 +365,19 @@ bool operator==(const MagnitudeRange &r) { return r.m_min == m_min && r.m_max == m_max; } - bool isSet() const { return (m_min != 0 || m_max != 0); } + bool isSet() const { return (m_min != 0.f || m_max != 0.f); } void set(float min, float max) { - m_min = convert(min); - m_max = convert(max); + m_min = min; + m_max = max; if (m_max < m_min) m_max = m_min; } bool sample(float f) { - unsigned int ui = convert(f); bool changed = false; if (isSet()) { - if (ui < m_min) { m_min = ui; changed = true; } - if (ui > m_max) { m_max = ui; changed = true; } + if (f < m_min) { m_min = f; changed = true; } + if (f > m_max) { m_max = f; changed = true; } } else { - m_max = m_min = ui; + m_max = m_min = f; changed = true; } return changed; @@ -409,40 +394,40 @@ } return changed; } - float getMin() const { return float(m_min) / UINT_MAX; } - float getMax() const { return float(m_max) / UINT_MAX; } + float getMin() const { return m_min; } + float getMax() const { return m_max; } private: - unsigned int m_min; - unsigned int m_max; - unsigned int convert(float f) { - if (f < 0.f) f = 0.f; - if (f > 1.f) f = 1.f; - return (unsigned int)(f * UINT_MAX); - } + float m_min; + float m_max; }; - typedef std::map ViewMagMap; + typedef std::map ViewMagMap; // key is view id mutable ViewMagMap m_viewMags; mutable std::vector m_columnMags; void invalidateMagnitudes(); - bool updateViewMagnitudes(View *v) const; - bool paintDrawBuffer(View *v, int w, int h, - int *binforx, float *binfory, - bool usePeaksCache, - MagnitudeRange &overallMag, - bool &overallMagChanged) const; - bool paintDrawBufferPeakFrequencies(View *v, int w, int h, - int *binforx, - int minbin, - int maxbin, - float displayMinFreq, - float displayMaxFreq, - bool logarithmic, - MagnitudeRange &overallMag, - bool &overallMagChanged) const; + bool updateViewMagnitudes(LayerGeometryProvider *v) const; + int paintDrawBuffer(LayerGeometryProvider *v, int w, int h, + const std::vector &binforx, + const std::vector &binfory, + bool usePeaksCache, + MagnitudeRange &overallMag, + bool &overallMagChanged, + bool rightToLeft, + double softTimeLimit) const; + int paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, int w, int h, + const std::vector &binforx, + int minbin, + int maxbin, + double displayMinFreq, + double displayMaxFreq, + bool logarithmic, + MagnitudeRange &overallMag, + bool &overallMagChanged, + bool rightToLeft, + double softTimeLimit) const; - virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const; - virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const; + virtual void updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const; + virtual void setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const; }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/SpectrumLayer.cpp --- a/layer/SpectrumLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/SpectrumLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -112,21 +112,15 @@ m_windowType, m_windowSize, getWindowIncrement(), - m_windowSize, - false, - StorageAdviser::Criteria - (StorageAdviser::SpeedCritical | - StorageAdviser::FrequentLookupLikely)); + m_windowSize); setSliceableModel(newFFT); m_biasCurve.clear(); - for (size_t i = 0; i < m_windowSize; ++i) { + for (int i = 0; i < m_windowSize; ++i) { m_biasCurve.push_back(1.f / (float(m_windowSize)/2.f)); } - newFFT->resume(); - m_newFFTNeeded = false; } @@ -257,7 +251,7 @@ } void -SpectrumLayer::setWindowSize(size_t ws) +SpectrumLayer::setWindowSize(int ws) { if (m_windowSize == ws) return; m_windowSize = ws; @@ -266,7 +260,7 @@ } void -SpectrumLayer::setWindowHopLevel(size_t v) +SpectrumLayer::setWindowHopLevel(int v) { if (m_windowHopLevel == v) return; m_windowHopLevel = v; @@ -301,42 +295,42 @@ } bool -SpectrumLayer::getValueExtents(float &, float &, bool &, QString &) const +SpectrumLayer::getValueExtents(double &, double &, bool &, QString &) const { return false; } -float -SpectrumLayer::getXForBin(int bin, int totalBins, float w) const +double +SpectrumLayer::getXForBin(int bin, int totalBins, double w) const { if (!m_sliceableModel) return SliceLayer::getXForBin(bin, totalBins, w); - float sampleRate = m_sliceableModel->getSampleRate(); - float binfreq = (sampleRate * bin) / (totalBins * 2); + sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); + double binfreq = (sampleRate * bin) / (totalBins * 2); return getXForFrequency(binfreq, w); } int -SpectrumLayer::getBinForX(float x, int totalBins, float w) const +SpectrumLayer::getBinForX(double x, int totalBins, double w) const { if (!m_sliceableModel) return SliceLayer::getBinForX(x, totalBins, w); - float sampleRate = m_sliceableModel->getSampleRate(); - float binfreq = getFrequencyForX(x, w); + sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); + double binfreq = getFrequencyForX(x, w); return int((binfreq * totalBins * 2) / sampleRate); } -float -SpectrumLayer::getFrequencyForX(float x, float w) const +double +SpectrumLayer::getFrequencyForX(double x, double w) const { - float freq = 0; + double freq = 0; if (!m_sliceableModel) return 0; - int sampleRate = m_sliceableModel->getSampleRate(); + sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - float maxfreq = float(sampleRate) / 2; + double maxfreq = double(sampleRate) / 2; switch (m_binScale) { @@ -345,26 +339,26 @@ break; case LogBins: - freq = powf(10.f, (x * log10f(maxfreq)) / w); + freq = pow(10.0, (x * log10(maxfreq)) / w); break; case InvertedLogBins: - freq = maxfreq - powf(10.f, ((w - x) * log10f(maxfreq)) / w); + freq = maxfreq - pow(10.0, ((w - x) * log10(maxfreq)) / w); break; } return freq; } -float -SpectrumLayer::getXForFrequency(float freq, float w) const +double +SpectrumLayer::getXForFrequency(double freq, double w) const { - float x = 0; + double x = 0; if (!m_sliceableModel) return x; - int sampleRate = m_sliceableModel->getSampleRate(); + sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - float maxfreq = float(sampleRate) / 2; + double maxfreq = double(sampleRate) / 2; switch (m_binScale) { @@ -373,12 +367,12 @@ break; case LogBins: - x = (log10f(freq) * w) / log10f(maxfreq); + x = (log10(freq) * w) / log10(maxfreq); break; case InvertedLogBins: if (maxfreq == freq) x = w; - else x = w - (log10f(maxfreq - freq) * w) / log10f(maxfreq); + else x = w - (log10(maxfreq - freq) * w) / log10(maxfreq); break; } @@ -386,26 +380,26 @@ } bool -SpectrumLayer::getXScaleValue(const View *v, int x, - float &value, QString &unit) const +SpectrumLayer::getXScaleValue(const LayerGeometryProvider *v, int x, + double &value, QString &unit) const { if (m_xorigins.find(v) == m_xorigins.end()) return false; int xorigin = m_xorigins.find(v)->second; - value = getFrequencyForX(x - xorigin, v->width() - xorigin - 1); + value = getFrequencyForX(x - xorigin, v->getPaintWidth() - xorigin - 1); unit = "Hz"; return true; } bool -SpectrumLayer::getYScaleValue(const View *v, int y, - float &value, QString &unit) const +SpectrumLayer::getYScaleValue(const LayerGeometryProvider *v, int y, + double &value, QString &unit) const { value = getValueForY(y, v); if (m_energyScale == dBScale || m_energyScale == MeterScale) { - if (value > 0.f) { - value = 10.f * log10f(value); + if (value > 0.0) { + value = 10.0 * log10(value); if (value < m_threshold) value = m_threshold; } else value = m_threshold; @@ -419,8 +413,8 @@ } bool -SpectrumLayer::getYScaleDifference(const View *v, int y0, int y1, - float &diff, QString &unit) const +SpectrumLayer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1, + double &diff, QString &unit) const { bool rv = SliceLayer::getYScaleDifference(v, y0, y1, diff, unit); if (rv && (unit == "dBV")) unit = "dB"; @@ -429,14 +423,14 @@ bool -SpectrumLayer::getCrosshairExtents(View *v, QPainter &paint, +SpectrumLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint, QPoint cursorPos, std::vector &extents) const { - QRect vertical(cursorPos.x(), cursorPos.y(), 1, v->height() - cursorPos.y()); + QRect vertical(cursorPos.x(), cursorPos.y(), 1, v->getPaintHeight() - cursorPos.y()); extents.push_back(vertical); - QRect horizontal(0, cursorPos.y(), v->width(), 12); + QRect horizontal(0, cursorPos.y(), v->getPaintWidth(), 12); extents.push_back(horizontal); int hoffset = 2; @@ -455,14 +449,14 @@ extents.push_back(log); QRect freq(cursorPos.x(), - v->height() - paint.fontMetrics().height() - hoffset, + v->getPaintHeight() - paint.fontMetrics().height() - hoffset, paint.fontMetrics().width("123456 Hz") + 2, paint.fontMetrics().height()); extents.push_back(freq); int w(paint.fontMetrics().width("C#10+50c") + 2); QRect pitch(cursorPos.x() - w, - v->height() - paint.fontMetrics().height() - hoffset, + v->getPaintHeight() - paint.fontMetrics().height() - hoffset, w, paint.fontMetrics().height()); extents.push_back(pitch); @@ -471,7 +465,7 @@ } void -SpectrumLayer::paintCrosshairs(View *v, QPainter &paint, +SpectrumLayer::paintCrosshairs(LayerGeometryProvider *v, QPainter &paint, QPoint cursorPos) const { if (!m_sliceableModel) return; @@ -487,19 +481,19 @@ paint.setPen(mapper.getContrastingColour()); int xorigin = m_xorigins[v]; - int w = v->width() - xorigin - 1; + int w = v->getPaintWidth() - xorigin - 1; - paint.drawLine(xorigin, cursorPos.y(), v->width(), cursorPos.y()); - paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->height()); + paint.drawLine(xorigin, cursorPos.y(), v->getPaintWidth(), cursorPos.y()); + paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->getPaintHeight()); - float fundamental = getFrequencyForX(cursorPos.x() - xorigin, w); + double fundamental = getFrequencyForX(cursorPos.x() - xorigin, w); int hoffset = 2; if (m_binScale == LogBins) hoffset = 13; v->drawVisibleText(paint, cursorPos.x() + 2, - v->height() - 2 - hoffset, + v->getPaintHeight() - 2 - hoffset, QString("%1 Hz").arg(fundamental), View::OutlinedText); @@ -507,15 +501,15 @@ QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); v->drawVisibleText(paint, cursorPos.x() - paint.fontMetrics().width(pitchLabel) - 2, - v->height() - 2 - hoffset, + v->getPaintHeight() - 2 - hoffset, pitchLabel, View::OutlinedText); } - float value = getValueForY(cursorPos.y(), v); - float thresh = m_threshold; - float db = thresh; - if (value > 0.f) db = 10.f * log10f(value); + double value = getValueForY(cursorPos.y(), v); + double thresh = m_threshold; + double db = thresh; + if (value > 0.0) db = 10.0 * log10(value); if (db < thresh) db = thresh; v->drawVisibleText(paint, @@ -534,10 +528,10 @@ while (harmonic < 100) { - float hx = lrintf(getXForFrequency(fundamental * harmonic, w)); + int hx = int(lrint(getXForFrequency(fundamental * harmonic, w))); hx += xorigin; - if (hx < xorigin || hx > v->width()) break; + if (hx < xorigin || hx > v->getPaintWidth()) break; int len = 7; @@ -549,9 +543,9 @@ } } - paint.drawLine(int(hx), + paint.drawLine(hx, cursorPos.y(), - int(hx), + hx, cursorPos.y() + len); ++harmonic; @@ -561,31 +555,31 @@ } QString -SpectrumLayer::getFeatureDescription(View *v, QPoint &p) const +SpectrumLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &p) const { if (!m_sliceableModel) return ""; int minbin = 0, maxbin = 0, range = 0; - QString genericDesc = SliceLayer::getFeatureDescription + QString genericDesc = SliceLayer::getFeatureDescriptionAux (v, p, false, minbin, maxbin, range); if (genericDesc == "") return ""; - float minvalue = 0.f; + double minvalue = 0.f; if (minbin < int(m_values.size())) minvalue = m_values[minbin]; - float maxvalue = minvalue; + double maxvalue = minvalue; if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin]; if (minvalue > maxvalue) std::swap(minvalue, maxvalue); QString binstr; QString hzstr; - int minfreq = lrintf((minbin * m_sliceableModel->getSampleRate()) / - m_windowSize); - int maxfreq = lrintf((std::max(maxbin, minbin+1) - * m_sliceableModel->getSampleRate()) / - m_windowSize); + int minfreq = int(lrint((minbin * m_sliceableModel->getSampleRate()) / + m_windowSize)); + int maxfreq = int(lrint((std::max(maxbin, minbin+1) + * m_sliceableModel->getSampleRate()) / + m_windowSize)); if (maxbin != minbin) { binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1); @@ -606,21 +600,21 @@ } QString dbstr; - float mindb = AudioLevel::multiplier_to_dB(minvalue); - float maxdb = AudioLevel::multiplier_to_dB(maxvalue); + double mindb = AudioLevel::multiplier_to_dB(minvalue); + double maxdb = AudioLevel::multiplier_to_dB(maxvalue); QString mindbstr; QString maxdbstr; if (mindb == AudioLevel::DB_FLOOR) { mindbstr = tr("-Inf"); } else { - mindbstr = QString("%1").arg(lrintf(mindb)); + mindbstr = QString("%1").arg(lrint(mindb)); } if (maxdb == AudioLevel::DB_FLOOR) { maxdbstr = tr("-Inf"); } else { - maxdbstr = QString("%1").arg(lrintf(maxdb)); + maxdbstr = QString("%1").arg(lrint(maxdb)); } - if (lrintf(mindb) != lrintf(maxdb)) { + if (lrint(mindb) != lrint(maxdb)) { dbstr = tr("%1 - %2").arg(mindbstr).arg(maxdbstr); } else { dbstr = tr("%1").arg(mindbstr); @@ -650,7 +644,7 @@ } void -SpectrumLayer::paint(View *v, QPainter &paint, QRect rect) const +SpectrumLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_originModel || !m_originModel->isOK() || !m_originModel->isReady()) { @@ -666,10 +660,10 @@ FFTModel *fft = dynamic_cast (const_cast(m_sliceableModel)); - float thresh = (powf(10, -6) / m_gain) * (m_windowSize / 2.f); // -60dB adj + double thresh = (pow(10, -6) / m_gain) * (m_windowSize / 2.0); // -60dB adj int xorigin = getVerticalScaleWidth(v, false, paint) + 1; - int w = v->width() - xorigin - 1; + int w = v->getPaintWidth() - xorigin - 1; int pkh = 0; //!!! if (m_binScale == LogBins) { @@ -684,7 +678,7 @@ // SVDEBUG << "Showing peaks..." << endl; - size_t col = v->getCentreFrame() / fft->getResolution(); + int col = int(v->getCentreFrame() / fft->getResolution()); paint.save(); paint.setRenderHint(QPainter::Antialiasing, false); @@ -692,8 +686,8 @@ int peakminbin = 0; int peakmaxbin = fft->getHeight() - 1; - float peakmaxfreq = Pitch::getFrequencyForPitch(128); - peakmaxbin = ((peakmaxfreq * fft->getHeight() * 2) / fft->getSampleRate()); + double peakmaxfreq = Pitch::getFrequencyForPitch(128); + peakmaxbin = int(((peakmaxfreq * fft->getHeight() * 2) / fft->getSampleRate())); FFTModel::PeakSet peaks = fft->getPeakFrequencies (FFTModel::MajorPitchAdaptivePeaks, col, peakminbin, peakmaxbin); @@ -702,12 +696,12 @@ BiasCurve curve; getBiasCurve(curve); - size_t cs = curve.size(); + int cs = int(curve.size()); - std::vector values; + std::vector values; - for (size_t bin = 0; bin < fft->getHeight(); ++bin) { - float value = m_sliceableModel->getValueAt(col, bin); + for (int bin = 0; bin < fft->getHeight(); ++bin) { + double value = m_sliceableModel->getValueAt(col, bin); if (bin < cs) value *= curve[bin]; values.push_back(value); } @@ -715,21 +709,21 @@ for (FFTModel::PeakSet::iterator i = peaks.begin(); i != peaks.end(); ++i) { - size_t bin = i->first; + int bin = i->first; // cerr << "bin = " << bin << ", thresh = " << thresh << ", value = " << fft->getMagnitudeAt(col, bin) << endl; - if (!fft->isOverThreshold(col, bin, thresh)) continue; + if (!fft->isOverThreshold(col, bin, float(thresh))) continue; - float freq = i->second; + double freq = i->second; - int x = lrintf(getXForFrequency(freq, w)); + int x = int(lrint(getXForFrequency(freq, w))); - float norm = 0.f; - float y = getYForValue(values[bin], v, norm); // don't need y, need norm + double norm = 0.f; + (void)getYForValue(values[bin], v, norm); // don't need return value, need norm paint.setPen(mapper.map(norm)); - paint.drawLine(xorigin + x, 0, xorigin + x, v->height() - pkh - 1); + paint.drawLine(xorigin + x, 0, xorigin + x, v->getPaintHeight() - pkh - 1); } paint.restore(); @@ -749,7 +743,7 @@ // if (m_binScale == LogBins) { // int pkh = 10; - int h = v->height(); + int h = v->getPaintHeight(); // piano keyboard //!!! should be in a new paintHorizontalScale()? @@ -762,8 +756,8 @@ for (int i = 0; i < 128; ++i) { - float f = Pitch::getFrequencyForPitch(i); - int x = lrintf(getXForFrequency(f, w)); + double f = Pitch::getFrequencyForPitch(i); + int x = int(lrint(getXForFrequency(f, w))); x += xorigin; @@ -805,7 +799,7 @@ if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) { // black notes paint.drawLine(x, h - pkh, x, h); - int rw = lrintf(float(x - px) / 4) * 2; + int rw = int(lrint(double(x - px) / 4) * 2); if (rw < 2) rw = 2; paint.drawRect(x - rw/2, h - pkh, rw, pkh/2); } else if (n == 0 || n == 5) { @@ -850,10 +844,10 @@ bool ok = false; - size_t windowSize = attributes.value("windowSize").toUInt(&ok); + int windowSize = attributes.value("windowSize").toUInt(&ok); if (ok) setWindowSize(windowSize); - size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); + int windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); if (ok) setWindowHopLevel(windowHopLevel); bool showPeaks = (attributes.value("showPeaks").trimmed() == "true"); diff -r 282f4be8f058 -r c02c51ae5238 layer/SpectrumLayer.h --- a/layer/SpectrumLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/SpectrumLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -39,13 +39,13 @@ void setModel(DenseTimeValueModel *model); virtual const Model *getModel() const { return m_originModel; } - virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos, + virtual bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint cursorPos, std::vector &extents) const; - virtual void paintCrosshairs(View *, QPainter &, QPoint) const; + virtual void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; virtual VerticalPosition getPreferredFrameCountPosition() const { return PositionTop; @@ -64,28 +64,28 @@ virtual void setProperty(const PropertyName &, int value); virtual void setProperties(const QXmlAttributes &); - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const; - virtual bool getXScaleValue(const View *v, int x, - float &value, QString &unit) const; + virtual bool getXScaleValue(const LayerGeometryProvider *v, int x, + double &value, QString &unit) const; - virtual bool getYScaleValue(const View *, int y, - float &value, QString &unit) const; + virtual bool getYScaleValue(const LayerGeometryProvider *, int y, + double &value, QString &unit) const; - virtual bool getYScaleDifference(const View *, int y0, int y1, - float &diff, QString &unit) const; + virtual bool getYScaleDifference(const LayerGeometryProvider *, int y0, int y1, + double &diff, QString &unit) const; - virtual bool isLayerScrollable(const View *) const { return false; } + virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; } void setChannel(int); int getChannel() const { return m_channel; } - void setWindowSize(size_t); - size_t getWindowSize() const { return m_windowSize; } + void setWindowSize(int); + int getWindowSize() const { return m_windowSize; } - void setWindowHopLevel(size_t level); - size_t getWindowHopLevel() const { return m_windowHopLevel; } + void setWindowHopLevel(int level); + int getWindowHopLevel() const { return m_windowHopLevel; } void setWindowType(WindowType type); WindowType getWindowType() const { return m_windowType; } @@ -93,7 +93,7 @@ void setShowPeaks(bool); bool getShowPeaks() const { return m_showPeaks; } - virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; } + virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; } virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; @@ -110,9 +110,9 @@ DenseTimeValueModel *m_originModel; int m_channel; bool m_channelSet; - size_t m_windowSize; + int m_windowSize; WindowType m_windowType; - size_t m_windowHopLevel; + int m_windowHopLevel; bool m_showPeaks; mutable bool m_newFFTNeeded; @@ -123,13 +123,13 @@ virtual void getBiasCurve(BiasCurve &) const; BiasCurve m_biasCurve; - virtual float getXForBin(int bin, int totalBins, float w) const; - virtual int getBinForX(float x, int totalBins, float w) const; + virtual double getXForBin(int bin, int totalBins, double w) const; + virtual int getBinForX(double x, int totalBins, double w) const; - float getFrequencyForX(float x, float w) const; - float getXForFrequency(float freq, float w) const; + double getFrequencyForX(double x, double w) const; + double getXForFrequency(double freq, double w) const; - size_t getWindowIncrement() const { + int getWindowIncrement() const { if (m_windowHopLevel == 0) return m_windowSize; else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4; else return m_windowSize / (1 << (m_windowHopLevel - 1)); diff -r 282f4be8f058 -r c02c51ae5238 layer/TextLayer.cpp --- a/layer/TextLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/TextLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -96,13 +96,13 @@ } bool -TextLayer::getValueExtents(float &, float &, bool &, QString &) const +TextLayer::getValueExtents(double &, double &, bool &, QString &) const { return false; } bool -TextLayer::isLayerScrollable(const View *v) const +TextLayer::isLayerScrollable(const LayerGeometryProvider *v) const { QPoint discard; return !v->shouldIlluminateLocalFeatures(this, discard); @@ -110,12 +110,12 @@ TextModel::PointList -TextLayer::getLocalPoints(View *v, int x, int y) const +TextLayer::getLocalPoints(LayerGeometryProvider *v, int x, int y) const { if (!m_model) return TextModel::PointList(); - long frame0 = v->getFrameForX(-150); - long frame1 = v->getFrameForX(v->width() + 150); + sv_frame_t frame0 = v->getFrameForX(-150); + sv_frame_t frame1 = v->getFrameForX(v->getPaintWidth() + 150); TextModel::PointList points(m_model->getPoints(frame0, frame1)); @@ -139,9 +139,9 @@ (QRect(0, 0, 150, 200), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, label); - if (py + rect.height() > v->height()) { - if (rect.height() > v->height()) py = 0; - else py = v->height() - rect.height() - 1; + if (py + rect.height() > v->getPaintHeight()) { + if (rect.height() > v->getPaintHeight()) py = 0; + else py = v->getPaintHeight() - rect.height() - 1; } if (x >= px && x < px + rect.width() && @@ -154,23 +154,23 @@ } bool -TextLayer::getPointToDrag(View *v, int x, int y, TextModel::Point &p) const +TextLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &p) const { if (!m_model) return false; - long a = v->getFrameForX(x - 120); - long b = v->getFrameForX(x + 10); + sv_frame_t a = v->getFrameForX(x - 120); + sv_frame_t b = v->getFrameForX(x + 10); TextModel::PointList onPoints = m_model->getPoints(a, b); if (onPoints.empty()) return false; - float nearestDistance = -1; + double nearestDistance = -1; for (TextModel::PointList::const_iterator i = onPoints.begin(); i != onPoints.end(); ++i) { - int yd = getYForHeight(v, (*i).height) - y; - int xd = v->getXForFrame((*i).frame) - x; - float distance = sqrtf(yd*yd + xd*xd); + double yd = getYForHeight(v, (*i).height) - y; + double xd = v->getXForFrame((*i).frame) - x; + double distance = sqrt(yd*yd + xd*xd); if (nearestDistance == -1 || distance < nearestDistance) { nearestDistance = distance; @@ -182,7 +182,7 @@ } QString -TextLayer::getFeatureDescription(View *v, QPoint &pos) const +TextLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); @@ -198,7 +198,7 @@ } } - long useFrame = points.begin()->frame; + sv_frame_t useFrame = points.begin()->frame; RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); @@ -220,8 +220,8 @@ //!!! too much overlap with TimeValueLayer/TimeInstantLayer bool -TextLayer::snapToFeatureFrame(View *v, int &frame, - size_t &resolution, +TextLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -240,7 +240,7 @@ } points = m_model->getPoints(frame, frame); - int snapped = frame; + sv_frame_t snapped = frame; bool found = false; for (TextModel::PointList::const_iterator i = points.begin(); @@ -292,32 +292,32 @@ } int -TextLayer::getYForHeight(View *v, float height) const +TextLayer::getYForHeight(LayerGeometryProvider *v, double height) const { - int h = v->height(); + int h = v->getPaintHeight(); return h - int(height * h); } -float -TextLayer::getHeightForY(View *v, int y) const +double +TextLayer::getHeightForY(LayerGeometryProvider *v, int y) const { - int h = v->height(); - return float(h - y) / h; + int h = v->getPaintHeight(); + return double(h - y) / h; } void -TextLayer::paint(View *v, QPainter &paint, QRect rect) const +TextLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) return; - int sampleRate = m_model->getSampleRate(); + sv_samplerate_t sampleRate = m_model->getSampleRate(); if (!sampleRate) return; // Profiler profiler("TextLayer::paint", true); int x0 = rect.left(), x1 = rect.right(); - long frame0 = v->getFrameForX(x0); - long frame1 = v->getFrameForX(x1); + sv_frame_t frame0 = v->getFrameForX(x0); + sv_frame_t frame1 = v->getFrameForX(x1); TextModel::PointList points(m_model->getPoints(frame0, frame1)); if (points.empty()) return; @@ -336,7 +336,7 @@ QPoint localPos; TextModel::Point illuminatePoint(0); - bool shouldIlluminate; + bool shouldIlluminate = false; if (v->shouldIlluminateLocalFeatures(this, localPos)) { shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), @@ -347,7 +347,7 @@ int boxMaxHeight = 200; paint.save(); - paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->height()); + paint.setClipRect(rect.x(), 0, rect.width() + boxMaxWidth, v->getPaintHeight()); for (TextModel::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { @@ -380,9 +380,9 @@ QRect textRect = QRect(3, 2, boxRect.width(), boxRect.height()); boxRect = QRect(0, 0, boxRect.width() + 6, boxRect.height() + 2); - if (y + boxRect.height() > v->height()) { - if (boxRect.height() > v->height()) y = 0; - else y = v->height() - boxRect.height() - 1; + if (y + boxRect.height() > v->getPaintHeight()) { + if (boxRect.height() > v->getPaintHeight()) y = 0; + else y = v->getPaintHeight() - boxRect.height() - 1; } boxRect = QRect(x, y, boxRect.width(), boxRect.height()); @@ -411,7 +411,7 @@ } void -TextLayer::drawStart(View *v, QMouseEvent *e) +TextLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; @@ -420,13 +420,13 @@ return; } - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - float height = getHeightForY(v, e->y()); + double height = getHeightForY(v, e->y()); - m_editingPoint = TextModel::Point(frame, height, ""); + m_editingPoint = TextModel::Point(frame, float(height), ""); m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); @@ -437,35 +437,35 @@ } void -TextLayer::drawDrag(View *v, QMouseEvent *e) +TextLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - float height = getHeightForY(v, e->y()); + double height = getHeightForY(v, e->y()); m_editingCommand->deletePoint(m_editingPoint); m_editingPoint.frame = frame; - m_editingPoint.height = height; + m_editingPoint.height = float(height); m_editingCommand->addPoint(m_editingPoint); } void -TextLayer::drawEnd(View *v, QMouseEvent *) +TextLayer::drawEnd(LayerGeometryProvider *v, QMouseEvent *) { // SVDEBUG << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; bool ok = false; - QString label = QInputDialog::getText(v, tr("Enter label"), - tr("Please enter a new label:"), + QString label = QInputDialog::getText(v->getView(), tr("Enter label"), + tr("Please enter a new label:"), QLineEdit::Normal, "", &ok); if (ok) { @@ -482,7 +482,7 @@ } void -TextLayer::eraseStart(View *v, QMouseEvent *e) +TextLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return; @@ -497,12 +497,12 @@ } void -TextLayer::eraseDrag(View *v, QMouseEvent *e) +TextLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *) { } void -TextLayer::eraseEnd(View *v, QMouseEvent *e) +TextLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; @@ -523,7 +523,7 @@ } void -TextLayer::editStart(View *v, QMouseEvent *e) +TextLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; @@ -545,21 +545,21 @@ } void -TextLayer::editDrag(View *v, QMouseEvent *e) +TextLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; - long frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); - float heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); + sv_frame_t frameDiff = v->getFrameForX(e->x()) - v->getFrameForX(m_editOrigin.x()); + double heightDiff = getHeightForY(v, e->y()) - getHeightForY(v, m_editOrigin.y()); - long frame = m_originalPoint.frame + frameDiff; - float height = m_originalPoint.height + heightDiff; + sv_frame_t frame = m_originalPoint.frame + frameDiff; + double height = m_originalPoint.height + heightDiff; -// long frame = v->getFrameForX(e->x()); +// sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = (frame / m_model->getResolution()) * m_model->getResolution(); -// float height = getHeightForY(v, e->y()); +// double height = getHeightForY(v, e->y()); if (!m_editingCommand) { m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label")); @@ -567,12 +567,12 @@ m_editingCommand->deletePoint(m_editingPoint); m_editingPoint.frame = frame; - m_editingPoint.height = height; + m_editingPoint.height = float(height); m_editingCommand->addPoint(m_editingPoint); } void -TextLayer::editEnd(View *, QMouseEvent *) +TextLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) { // SVDEBUG << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; @@ -600,7 +600,7 @@ } bool -TextLayer::editOpen(View *v, QMouseEvent *e) +TextLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return false; @@ -610,7 +610,7 @@ QString label = text.label; bool ok = false; - label = QInputDialog::getText(v, tr("Enter label"), + label = QInputDialog::getText(v->getView(), tr("Enter label"), tr("Please enter a new label:"), QLineEdit::Normal, label, &ok); if (ok && label != text.label) { @@ -623,7 +623,7 @@ } void -TextLayer::moveSelection(Selection s, size_t newStartFrame) +TextLayer::moveSelection(Selection s, sv_frame_t newStartFrame) { if (!m_model) return; @@ -667,9 +667,9 @@ if (s.contains(i->frame)) { - double target = i->frame; - target = newSize.getStartFrame() + - double(target - s.getStartFrame()) * ratio; + double target = double(i->frame); + target = double(newSize.getStartFrame()) + + target - double(s.getStartFrame()) * ratio; TextModel::Point newPoint(*i); newPoint.frame = lrint(target); @@ -701,7 +701,7 @@ } void -TextLayer::copy(View *v, Selection s, Clipboard &to) +TextLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -719,7 +719,7 @@ } bool -TextLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */) +TextLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */) { if (!m_model) return false; @@ -730,7 +730,7 @@ if (clipboardHasDifferentAlignment(v, from)) { QMessageBox::StandardButton button = - QMessageBox::question(v, tr("Re-align pasted items?"), + QMessageBox::question(v->getView(), tr("Re-align pasted items?"), tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); @@ -747,7 +747,7 @@ TextModel::EditCommand *command = new TextModel::EditCommand(m_model, tr("Paste")); - float valueMin = 0.0, valueMax = 1.0; + double valueMin = 0.0, valueMax = 1.0; for (Clipboard::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { if (i->haveValue()) { @@ -761,7 +761,7 @@ i != points.end(); ++i) { if (!i->haveFrame()) continue; - size_t frame = 0; + sv_frame_t frame = 0; if (!realign) { @@ -780,9 +780,9 @@ TextModel::Point newPoint(frame); if (i->haveValue()) { - newPoint.height = (i->getValue() - valueMin) / (valueMax - valueMin); + newPoint.height = float((i->getValue() - valueMin) / (valueMax - valueMin)); } else { - newPoint.height = 0.5; + newPoint.height = 0.5f; } if (i->haveLabel()) { diff -r 282f4be8f058 -r c02c51ae5238 layer/TextLayer.h --- a/layer/TextLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/TextLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -32,35 +32,35 @@ public: TextLayer(); - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; - virtual bool snapToFeatureFrame(View *v, int &frame, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual void drawStart(View *v, QMouseEvent *); - virtual void drawDrag(View *v, QMouseEvent *); - virtual void drawEnd(View *v, QMouseEvent *); + virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void eraseStart(View *v, QMouseEvent *); - virtual void eraseDrag(View *v, QMouseEvent *); - virtual void eraseEnd(View *v, QMouseEvent *); + virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void editStart(View *v, QMouseEvent *); - virtual void editDrag(View *v, QMouseEvent *); - virtual void editEnd(View *v, QMouseEvent *); + virtual void editStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void moveSelection(Selection s, size_t newStartFrame); + virtual void moveSelection(Selection s, sv_frame_t newStartFrame); virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(View *v, Selection s, Clipboard &to); - virtual bool paste(View *v, const Clipboard &from, int frameOffset, + virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to); + virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive); - virtual bool editOpen(View *, QMouseEvent *); // on double-click + virtual bool editOpen(LayerGeometryProvider *, QMouseEvent *); // on double-click virtual const Model *getModel() const { return m_model; } void setModel(TextModel *model); @@ -74,16 +74,16 @@ int value) const; virtual void setProperty(const PropertyName &, int value); - virtual bool isLayerScrollable(const View *v) const; + virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; virtual bool isLayerEditable() const { return true; } - virtual int getCompletion(View *) const { return m_model->getCompletion(); } + virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); } - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const; - virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; } + virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; } virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; @@ -91,14 +91,14 @@ void setProperties(const QXmlAttributes &attributes); protected: - int getYForHeight(View *v, float height) const; - float getHeightForY(View *v, int y) const; + int getYForHeight(LayerGeometryProvider *v, double height) const; + double getHeightForY(LayerGeometryProvider *v, int y) const; virtual int getDefaultColourHint(bool dark, bool &impose); - TextModel::PointList getLocalPoints(View *v, int x, int y) const; + TextModel::PointList getLocalPoints(LayerGeometryProvider *v, int x, int y) const; - bool getPointToDrag(View *v, int x, int y, TextModel::Point &) const; + bool getPointToDrag(LayerGeometryProvider *v, int x, int y, TextModel::Point &) const; TextModel *m_model; bool m_editing; diff -r 282f4be8f058 -r c02c51ae5238 layer/TimeInstantLayer.cpp --- a/layer/TimeInstantLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/TimeInstantLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -147,14 +147,14 @@ } bool -TimeInstantLayer::isLayerScrollable(const View *v) const +TimeInstantLayer::isLayerScrollable(const LayerGeometryProvider *v) const { QPoint discard; return !v->shouldIlluminateLocalFeatures(this, discard); } SparseOneDimensionalModel::PointList -TimeInstantLayer::getLocalPoints(View *v, int x) const +TimeInstantLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { // Return a set of points that all have the same frame number, the // nearest to the given x coordinate, and that are within a @@ -162,7 +162,7 @@ if (!m_model) return SparseOneDimensionalModel::PointList(); - long frame = v->getFrameForX(x); + sv_frame_t frame = v->getFrameForX(x); SparseOneDimensionalModel::PointList onPoints = m_model->getPoints(frame); @@ -201,7 +201,7 @@ } QString -TimeInstantLayer::getLabelPreceding(size_t frame) const +TimeInstantLayer::getLabelPreceding(sv_frame_t frame) const { if (!m_model) return ""; SparseOneDimensionalModel::PointList points = m_model->getPreviousPoints(frame); @@ -213,7 +213,7 @@ } QString -TimeInstantLayer::getFeatureDescription(View *v, QPoint &pos) const +TimeInstantLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); @@ -229,7 +229,7 @@ } } - long useFrame = points.begin()->frame; + sv_frame_t useFrame = points.begin()->frame; RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); @@ -249,8 +249,8 @@ } bool -TimeInstantLayer::snapToFeatureFrame(View *v, int &frame, - size_t &resolution, +TimeInstantLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -269,7 +269,7 @@ } points = m_model->getPoints(frame, frame); - int snapped = frame; + sv_frame_t snapped = frame; bool found = false; for (SparseOneDimensionalModel::PointList::const_iterator i = points.begin(); @@ -321,7 +321,7 @@ } void -TimeInstantLayer::paint(View *v, QPainter &paint, QRect rect) const +TimeInstantLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) return; @@ -329,8 +329,8 @@ int x0 = rect.left(), x1 = rect.right(); - long frame0 = v->getFrameForX(x0); - long frame1 = v->getFrameForX(x1); + sv_frame_t frame0 = v->getFrameForX(x0); + sv_frame_t frame1 = v->getFrameForX(x1); SparseOneDimensionalModel::PointList points(m_model->getPoints (frame0, frame1)); @@ -367,7 +367,7 @@ // << m_model->getResolution() << " frames" << endl; QPoint localPos; - long illuminateFrame = -1; + sv_frame_t illuminateFrame = -1; if (v->shouldIlluminateLocalFeatures(this, localPos)) { SparseOneDimensionalModel::PointList localPoints = @@ -403,16 +403,16 @@ } if (p.frame == illuminateFrame) { - paint.setPen(getForegroundQColor(v)); + paint.setPen(getForegroundQColor(v->getView())); } else { paint.setPen(brushColour); } if (m_plotStyle == PlotInstants) { if (iw > 1) { - paint.drawRect(x, 0, iw - 1, v->height() - 1); + paint.drawRect(x, 0, iw - 1, v->getPaintHeight() - 1); } else { - paint.drawLine(x, 0, x, v->height() - 1); + paint.drawLine(x, 0, x, v->getPaintHeight() - 1); } } else { @@ -431,11 +431,11 @@ if (nx >= x) { if (illuminateFrame != p.frame && - (nx < x + 5 || x >= v->width() - 1)) { + (nx < x + 5 || x >= v->getPaintWidth() - 1)) { paint.setPen(Qt::NoPen); } - paint.drawRect(x, -1, nx - x, v->height() + 1); + paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1); } odd = !odd; @@ -466,7 +466,7 @@ } void -TimeInstantLayer::drawStart(View *v, QMouseEvent *e) +TimeInstantLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) { #ifdef DEBUG_TIME_INSTANT_LAYER cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << endl; @@ -474,7 +474,7 @@ if (!m_model) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); @@ -489,7 +489,7 @@ } void -TimeInstantLayer::drawDrag(View *v, QMouseEvent *e) +TimeInstantLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) { #ifdef DEBUG_TIME_INSTANT_LAYER cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << endl; @@ -497,7 +497,7 @@ if (!m_model || !m_editing) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); m_editingCommand->deletePoint(m_editingPoint); @@ -506,7 +506,7 @@ } void -TimeInstantLayer::drawEnd(View *, QMouseEvent *e) +TimeInstantLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *) { #ifdef DEBUG_TIME_INSTANT_LAYER cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << endl; @@ -523,7 +523,7 @@ } void -TimeInstantLayer::eraseStart(View *v, QMouseEvent *e) +TimeInstantLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return; @@ -541,12 +541,12 @@ } void -TimeInstantLayer::eraseDrag(View *v, QMouseEvent *e) +TimeInstantLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *) { } void -TimeInstantLayer::eraseEnd(View *v, QMouseEvent *e) +TimeInstantLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; @@ -567,7 +567,7 @@ } void -TimeInstantLayer::editStart(View *v, QMouseEvent *e) +TimeInstantLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) { #ifdef DEBUG_TIME_INSTANT_LAYER cerr << "TimeInstantLayer::editStart(" << e->x() << ")" << endl; @@ -589,7 +589,7 @@ } void -TimeInstantLayer::editDrag(View *v, QMouseEvent *e) +TimeInstantLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) { #ifdef DEBUG_TIME_INSTANT_LAYER cerr << "TimeInstantLayer::editDrag(" << e->x() << ")" << endl; @@ -597,7 +597,7 @@ if (!m_model || !m_editing) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); @@ -612,7 +612,7 @@ } void -TimeInstantLayer::editEnd(View *, QMouseEvent *e) +TimeInstantLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) { #ifdef DEBUG_TIME_INSTANT_LAYER cerr << "TimeInstantLayer::editEnd(" << e->x() << ")" << endl; @@ -631,7 +631,7 @@ } bool -TimeInstantLayer::editOpen(View *v, QMouseEvent *e) +TimeInstantLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return false; @@ -666,7 +666,7 @@ } void -TimeInstantLayer::moveSelection(Selection s, size_t newStartFrame) +TimeInstantLayer::moveSelection(Selection s, sv_frame_t newStartFrame) { if (!m_model) return; @@ -712,9 +712,9 @@ if (s.contains(i->frame)) { - double target = i->frame; - target = newSize.getStartFrame() + - double(target - s.getStartFrame()) * ratio; + double target = double(i->frame); + target = double(newSize.getStartFrame()) + + target - double(s.getStartFrame()) * ratio; SparseOneDimensionalModel::Point newPoint(*i); newPoint.frame = lrint(target); @@ -747,7 +747,7 @@ } void -TimeInstantLayer::copy(View *v, Selection s, Clipboard &to) +TimeInstantLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -765,7 +765,7 @@ } bool -TimeInstantLayer::paste(View *v, const Clipboard &from, int frameOffset, bool) +TimeInstantLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool) { if (!m_model) return false; @@ -776,7 +776,7 @@ if (clipboardHasDifferentAlignment(v, from)) { QMessageBox::StandardButton button = - QMessageBox::question(v, tr("Re-align pasted instants?"), + QMessageBox::question(v->getView(), tr("Re-align pasted instants?"), tr("The instants you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); @@ -798,7 +798,7 @@ if (!i->haveFrame()) continue; - size_t frame = 0; + sv_frame_t frame = 0; if (!realign) { diff -r 282f4be8f058 -r c02c51ae5238 layer/TimeInstantLayer.h --- a/layer/TimeInstantLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/TimeInstantLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -33,35 +33,35 @@ TimeInstantLayer(); virtual ~TimeInstantLayer(); - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual QString getLabelPreceding(size_t) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getLabelPreceding(sv_frame_t) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; - virtual bool snapToFeatureFrame(View *v, int &frame, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual void drawStart(View *v, QMouseEvent *); - virtual void drawDrag(View *v, QMouseEvent *); - virtual void drawEnd(View *v, QMouseEvent *); + virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void eraseStart(View *v, QMouseEvent *); - virtual void eraseDrag(View *v, QMouseEvent *); - virtual void eraseEnd(View *v, QMouseEvent *); + virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void editStart(View *v, QMouseEvent *); - virtual void editDrag(View *v, QMouseEvent *); - virtual void editEnd(View *v, QMouseEvent *); + virtual void editStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual bool editOpen(View *, QMouseEvent *); + virtual bool editOpen(LayerGeometryProvider *, QMouseEvent *); - virtual void moveSelection(Selection s, size_t newStartFrame); + virtual void moveSelection(Selection s, sv_frame_t newStartFrame); virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(View *v, Selection s, Clipboard &to); - virtual bool paste(View *v, const Clipboard &from, int frameOffset, + virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to); + virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive); virtual const Model *getModel() const { return m_model; } @@ -84,15 +84,15 @@ void setPlotStyle(PlotStyle style); PlotStyle getPlotStyle() const { return m_plotStyle; } - virtual bool isLayerScrollable(const View *v) const; + virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; virtual bool isLayerEditable() const { return true; } - virtual int getCompletion(View *) const { return m_model->getCompletion(); } + virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); } virtual bool needsTextLabelHeight() const { return m_model->hasTextLabels(); } - virtual bool getValueExtents(float &, float &, bool &, QString &) const { + virtual bool getValueExtents(double &, double &, bool &, QString &) const { return false; } @@ -109,14 +109,14 @@ } } - virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; } + virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; } protected: - SparseOneDimensionalModel::PointList getLocalPoints(View *v, int) const; + SparseOneDimensionalModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const; virtual int getDefaultColourHint(bool dark, bool &impose); - bool clipboardAlignmentDiffers(View *v, const Clipboard &) const; + bool clipboardAlignmentDiffers(LayerGeometryProvider *v, const Clipboard &) const; SparseOneDimensionalModel *m_model; bool m_editing; diff -r 282f4be8f058 -r c02c51ae5238 layer/TimeRulerLayer.cpp --- a/layer/TimeRulerLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/TimeRulerLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -26,12 +26,11 @@ #include #include +#include //#define DEBUG_TIME_RULER_LAYER 1 - - TimeRulerLayer::TimeRulerLayer() : SingleColourLayer(), m_model(0), @@ -49,8 +48,8 @@ } bool -TimeRulerLayer::snapToFeatureFrame(View *v, int &frame, - size_t &resolution, SnapType snap) const +TimeRulerLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { resolution = 1; @@ -60,7 +59,7 @@ bool q; int tick = getMajorTickSpacing(v, q); RealTime rtick = RealTime::fromMilliseconds(tick); - int rate = m_model->getSampleRate(); + sv_samplerate_t rate = m_model->getSampleRate(); RealTime rt = RealTime::frame2RealTime(frame, rate); double ratio = rt / rtick; @@ -68,9 +67,9 @@ int rounded = int(ratio); RealTime rdrt = rtick * rounded; - int left = RealTime::realTime2Frame(rdrt, rate); - resolution = RealTime::realTime2Frame(rtick, rate); - int right = left + resolution; + sv_frame_t left = RealTime::realTime2Frame(rdrt, rate); + resolution = int(RealTime::realTime2Frame(rtick, rate)); + sv_frame_t right = left + resolution; // SVDEBUG << "TimeRulerLayer::snapToFeatureFrame: type " // << int(snap) << ", frame " << frame << " (time " @@ -88,7 +87,7 @@ case SnapNearest: { - if (abs(frame - left) > abs(right - frame)) { + if (llabs(frame - left) > llabs(right - frame)) { frame = right; } else { frame = left; @@ -141,24 +140,24 @@ } int -TimeRulerLayer::getMajorTickSpacing(View *v, bool &quarterTicks) const +TimeRulerLayer::getMajorTickSpacing(LayerGeometryProvider *v, bool &quarterTicks) const { // return value is in milliseconds if (!m_model || !v) return 1000; - int sampleRate = m_model->getSampleRate(); + sv_samplerate_t sampleRate = m_model->getSampleRate(); if (!sampleRate) return 1000; - long startFrame = v->getStartFrame(); - long endFrame = v->getEndFrame(); + sv_frame_t startFrame = v->getStartFrame(); + sv_frame_t endFrame = v->getEndFrame(); int minPixelSpacing = 50; RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate); RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate); - int count = v->width() / minPixelSpacing; + int count = v->getPaintWidth() / minPixelSpacing; if (count < 1) count = 1; RealTime rtGap = (rtEnd - rtStart) / count; @@ -182,6 +181,8 @@ } else { incms = 1; int ms = rtGap.msec(); +// cerr << "rtGap.msec = " << ms << ", rtGap = " << rtGap << ", count = " << count << endl; +// cerr << "startFrame = " << startFrame << ", endFrame = " << endFrame << " rtStart = " << rtStart << ", rtEnd = " << rtEnd << endl; if (ms > 0) { incms *= 10; ms /= 10; } if (ms > 0) { incms *= 10; ms /= 10; } if (ms > 0) { incms *= 5; ms /= 5; } @@ -192,7 +193,7 @@ } void -TimeRulerLayer::paint(View *v, QPainter &paint, QRect rect) const +TimeRulerLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { #ifdef DEBUG_TIME_RULER_LAYER SVDEBUG << "TimeRulerLayer::paint (" << rect.x() << "," << rect.y() @@ -201,10 +202,10 @@ if (!m_model || !m_model->isOK()) return; - int sampleRate = m_model->getSampleRate(); + sv_samplerate_t sampleRate = m_model->getSampleRate(); if (!sampleRate) return; - long startFrame = v->getFrameForX(rect.x() - 50); + sv_frame_t startFrame = v->getFrameForX(rect.x() - 50); #ifdef DEBUG_TIME_RULER_LAYER cerr << "start frame = " << startFrame << endl; @@ -213,7 +214,7 @@ bool quarter = false; int incms = getMajorTickSpacing(v, quarter); - int ms = lrint(1000.0 * (double(startFrame) / double(sampleRate))); + int ms = int(lrint(1000.0 * (double(startFrame) / double(sampleRate)))); ms = (ms / incms) * incms - incms; #ifdef DEBUG_TIME_RULER_LAYER @@ -226,8 +227,8 @@ // draw the actual ticks or lines. int minPixelSpacing = 50; - long incFrame = (incms * sampleRate) / 1000; - int incX = incFrame / v->getZoomLevel(); + sv_frame_t incFrame = lrint((incms * sampleRate) / 1000); + int incX = int(incFrame / v->getZoomLevel()); int ticks = 10; if (incX < minPixelSpacing * 2) { ticks = quarter ? 4 : 5; @@ -237,6 +238,13 @@ paint.save(); + // Do not label time zero - we now overlay an opaque area over + // time < 0 which would cut it in half + int minlabel = 1; // ms + + // used for a sanity check + sv_frame_t prevframe = 0; + while (1) { // frame is used to determine where to draw the lines, so it @@ -245,14 +253,21 @@ // re-drawing with a different start frame). double dms = ms; - long frame = lrint((dms * sampleRate) / 1000.0); + sv_frame_t frame = lrint((dms * sampleRate) / 1000.0); frame /= v->getZoomLevel(); frame *= v->getZoomLevel(); // so frame corresponds to an exact pixel + if (frame == prevframe && prevframe != 0) { + cerr << "ERROR: frame == prevframe (== " << frame + << ") in TimeRulerLayer::paint" << endl; + throw std::logic_error("frame == prevframe in TimeRulerLayer::paint"); + } + prevframe = frame; + int x = v->getXForFrame(frame); #ifdef DEBUG_TIME_RULER_LAYER - SVDEBUG << "Considering frame = " << frame << ", x = " << x << endl; + cerr << "Considering frame = " << frame << ", x = " << x << endl; #endif if (x >= rect.x() + rect.width() + 50) { @@ -262,7 +277,7 @@ break; } - if (x >= rect.x() - 50) { + if (x >= rect.x() - 50 && ms >= minlabel) { RealTime rt = RealTime::fromMilliseconds(ms); @@ -283,11 +298,11 @@ } paint.setPen(greyColour); - paint.drawLine(x, 0, x, v->height()); + paint.drawLine(x, 0, x, v->getPaintHeight()); paint.setPen(getBaseQColor()); paint.drawLine(x, 0, x, 5); - paint.drawLine(x, v->height() - 6, x, v->height() - 1); + paint.drawLine(x, v->getPaintHeight() - 6, x, v->getPaintHeight() - 1); int y; switch (m_labelHeight) { @@ -296,16 +311,16 @@ y = 6 + metrics.ascent(); break; case LabelMiddle: - y = v->height() / 2 - metrics.height() / 2 + metrics.ascent(); + y = v->getPaintHeight() / 2 - metrics.height() / 2 + metrics.ascent(); break; case LabelBottom: - y = v->height() - metrics.height() + metrics.ascent() - 6; + y = v->getPaintHeight() - metrics.height() + metrics.ascent() - 6; } if (v->getViewManager() && v->getViewManager()->getOverlayMode() != ViewManager::NoOverlays) { - if (v->getLayer(0) == this) { + if (v->getView()->getLayer(0) == this) { // backmost layer, don't worry about outlining the text paint.drawText(x+2 - tw/2, y, text); } else { @@ -340,14 +355,14 @@ if (ticks == 10) { if ((i % 2) == 1) { if (i == 5) { - paint.drawLine(x, 0, x, v->height()); + paint.drawLine(x, 0, x, v->getPaintHeight()); } else sz = 3; } else { sz = 7; } } paint.drawLine(x, 0, x, sz); - paint.drawLine(x, v->height() - sz - 1, x, v->height() - 1); + paint.drawLine(x, v->getPaintHeight() - sz - 1, x, v->getPaintHeight() - 1); } ms += incms; diff -r 282f4be8f058 -r c02c51ae5238 layer/TimeRulerLayer.h --- a/layer/TimeRulerLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/TimeRulerLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -32,7 +32,7 @@ public: TimeRulerLayer(); - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; void setModel(Model *); virtual const Model *getModel() const { return m_model; } @@ -41,32 +41,34 @@ void setLabelHeight(LabelHeight h) { m_labelHeight = h; } LabelHeight getLabelHeight() const { return m_labelHeight; } - virtual bool snapToFeatureFrame(View *, int &, size_t &, SnapType) const; + virtual bool snapToFeatureFrame(LayerGeometryProvider *, sv_frame_t &, int &, SnapType) const; virtual ColourSignificance getLayerColourSignificance() const { return ColourIrrelevant; } - virtual bool getValueExtents(float &, float &, bool &, QString &) const { + virtual bool getValueExtents(double &, double &, bool &, QString &) const { return false; } virtual QString getLayerPresentationName() const; - virtual int getVerticalScaleWidth(View *, bool, QPainter &) const { return 0; } + virtual int getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &) const { return 0; } virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; void setProperties(const QXmlAttributes &attributes); + virtual bool canExistWithoutModel() const { return true; } + protected: Model *m_model; LabelHeight m_labelHeight; virtual int getDefaultColourHint(bool dark, bool &impose); - int getMajorTickSpacing(View *, bool &quarterTicks) const; + int getMajorTickSpacing(LayerGeometryProvider *, bool &quarterTicks) const; }; #endif diff -r 282f4be8f058 -r c02c51ae5238 layer/TimeValueLayer.cpp --- a/layer/TimeValueLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/TimeValueLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -20,6 +20,7 @@ #include "base/Profiler.h" #include "base/LogRange.h" #include "base/RangeMapper.h" +#include "base/Pitch.h" #include "ColourDatabase.h" #include "view/View.h" @@ -315,7 +316,7 @@ } bool -TimeValueLayer::isLayerScrollable(const View *v) const +TimeValueLayer::isLayerScrollable(const LayerGeometryProvider *v) const { // We don't illuminate sections in the line or curve modes, so // they're always scrollable @@ -329,7 +330,7 @@ } bool -TimeValueLayer::getValueExtents(float &min, float &max, +TimeValueLayer::getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const { if (!m_model) return false; @@ -342,7 +343,7 @@ unit = getScaleUnits(); if (m_derivative) { - max = std::max(fabsf(min), fabsf(max)); + max = std::max(fabs(min), fabs(max)); min = -max; } @@ -356,7 +357,7 @@ max = max + 0.5; min = min - 0.5; } else { - float margin = (max - min) / 10.0; + double margin = (max - min) / 10.0; max = max + margin; min = min - margin; } @@ -370,7 +371,7 @@ } bool -TimeValueLayer::getDisplayExtents(float &min, float &max) const +TimeValueLayer::getDisplayExtents(double &min, double &max) const { if (!m_model || shouldAutoAlign()) return false; @@ -384,7 +385,7 @@ } if (m_derivative) { - max = std::max(fabsf(min), fabsf(max)); + max = std::max(fabs(min), fabs(max)); min = -max; } @@ -396,7 +397,7 @@ } bool -TimeValueLayer::setDisplayExtents(float min, float max) +TimeValueLayer::setDisplayExtents(double min, double max) { if (!m_model) return false; @@ -438,7 +439,7 @@ RangeMapper *mapper = getNewVerticalZoomRangeMapper(); if (!mapper) return 0; - float dmin, dmax; + double dmin, dmax; getDisplayExtents(dmin, dmax); int nr = mapper->getPositionForValue(dmax - dmin); @@ -461,23 +462,23 @@ RangeMapper *mapper = getNewVerticalZoomRangeMapper(); if (!mapper) return; - float min, max; + double min, max; bool logarithmic; QString unit; getValueExtents(min, max, logarithmic, unit); - float dmin, dmax; + double dmin, dmax; getDisplayExtents(dmin, dmax); - float newdist = mapper->getValueForPosition(100 - step); + double newdist = mapper->getValueForPosition(100 - step); - float newmin, newmax; + double newmin, newmax; if (logarithmic) { // see SpectrogramLayer::setVerticalZoomStep - newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2; + newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2; newmin = newmax - newdist; #ifdef DEBUG_TIME_VALUE_LAYER @@ -485,7 +486,7 @@ #endif } else { - float dmid = (dmax + dmin) / 2; + double dmid = (dmax + dmin) / 2; newmin = dmid - newdist / 2; newmax = dmid + newdist / 2; } @@ -512,7 +513,7 @@ RangeMapper *mapper; - float min, max; + double min, max; bool logarithmic; QString unit; getValueExtents(min, max, logarithmic, unit); @@ -529,11 +530,11 @@ } SparseTimeValueModel::PointList -TimeValueLayer::getLocalPoints(View *v, int x) const +TimeValueLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { if (!m_model) return SparseTimeValueModel::PointList(); - long frame = v->getFrameForX(x); + sv_frame_t frame = v->getFrameForX(x); SparseTimeValueModel::PointList onPoints = m_model->getPoints(frame); @@ -553,7 +554,7 @@ usePoints = nextPoints; } else if (nextPoints.empty()) { // stick with prevPoints - } else if (long(prevPoints.begin()->frame) < v->getStartFrame() && + } else if (prevPoints.begin()->frame < v->getStartFrame() && !(nextPoints.begin()->frame > v->getEndFrame())) { usePoints = nextPoints; } else if (nextPoints.begin()->frame - frame < @@ -574,7 +575,7 @@ } QString -TimeValueLayer::getLabelPreceding(size_t frame) const +TimeValueLayer::getLabelPreceding(sv_frame_t frame) const { if (!m_model) return ""; SparseTimeValueModel::PointList points = m_model->getPreviousPoints(frame); @@ -586,7 +587,7 @@ } QString -TimeValueLayer::getFeatureDescription(View *v, QPoint &pos) const +TimeValueLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); @@ -602,24 +603,35 @@ } } - long useFrame = points.begin()->frame; + sv_frame_t useFrame = points.begin()->frame; RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate()); + QString valueText; + float value = points.begin()->value; + QString unit = getScaleUnits(); + + if (unit == "Hz") { + valueText = tr("%1 Hz (%2, %3)") + .arg(value) + .arg(Pitch::getPitchLabelForFrequency(value)) + .arg(Pitch::getPitchForFrequency(value)); + } else if (unit != "") { + valueText = tr("%1 %2").arg(value).arg(unit); + } else { + valueText = tr("%1").arg(value); + } + QString text; - QString unit = getScaleUnits(); - if (unit != "") unit = " " + unit; if (points.begin()->label == "") { - text = QString(tr("Time:\t%1\nValue:\t%2%3\nNo label")) + text = QString(tr("Time:\t%1\nValue:\t%2\nNo label")) .arg(rt.toText(true).c_str()) - .arg(points.begin()->value) - .arg(unit); + .arg(valueText); } else { - text = QString(tr("Time:\t%1\nValue:\t%2%3\nLabel:\t%4")) + text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%4")) .arg(rt.toText(true).c_str()) - .arg(points.begin()->value) - .arg(unit) + .arg(valueText) .arg(points.begin()->label); } @@ -629,8 +641,8 @@ } bool -TimeValueLayer::snapToFeatureFrame(View *v, int &frame, - size_t &resolution, +TimeValueLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -649,7 +661,7 @@ } points = m_model->getPoints(frame, frame); - int snapped = frame; + sv_frame_t snapped = frame; bool found = false; for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); @@ -701,8 +713,8 @@ } bool -TimeValueLayer::snapToSimilarFeature(View *v, int &frame, - size_t &resolution, +TimeValueLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const { if (!m_model) { @@ -716,8 +728,8 @@ SparseTimeValueModel::PointList::const_iterator i; - int matchframe = frame; - float matchvalue = 0.f; + sv_frame_t matchframe = frame; + double matchvalue = 0.0; for (i = close.begin(); i != close.end(); ++i) { if (i->frame > frame) break; @@ -725,27 +737,35 @@ matchframe = i->frame; } - int snapped = frame; + sv_frame_t snapped = frame; bool found = false; bool distant = false; - float epsilon = 0.0001; + double epsilon = 0.0001; i = close.begin(); // Scan through the close points first, then the more distant ones - // if no suitable close one is found + // if no suitable close one is found. So the while-termination + // condition here can only happen once i has passed through the + // whole of the close container and then the whole of the separate + // points container. The two iterators are totally distinct, but + // have the same type so we cheekily use the same variable and a + // single loop for both. while (i != points.end()) { - if (i == close.end()) { - i = points.begin(); - distant = true; + if (!distant) { + if (i == close.end()) { + // switch from the close container to the points container + i = points.begin(); + distant = true; + } } if (snap == SnapRight) { if (i->frame > matchframe && - fabsf(i->value - matchvalue) < epsilon) { + fabs(i->value - matchvalue) < epsilon) { snapped = i->frame; found = true; break; @@ -754,7 +774,7 @@ } else if (snap == SnapLeft) { if (i->frame < matchframe) { - if (fabsf(i->value - matchvalue) < epsilon) { + if (fabs(i->value - matchvalue) < epsilon) { snapped = i->frame; found = true; // don't break, as the next may be better } @@ -774,7 +794,7 @@ } void -TimeValueLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const +TimeValueLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const { min = 0.0; max = 0.0; @@ -810,11 +830,11 @@ } int -TimeValueLayer::getYForValue(View *v, float val) const +TimeValueLayer::getYForValue(LayerGeometryProvider *v, double val) const { - float min = 0.0, max = 0.0; + double min = 0.0, max = 0.0; bool logarithmic = false; - int h = v->height(); + int h = v->getPaintHeight(); getScaleExtents(v, min, max, logarithmic); @@ -830,16 +850,16 @@ return int(h - ((val - min) * h) / (max - min)); } -float -TimeValueLayer::getValueForY(View *v, int y) const +double +TimeValueLayer::getValueForY(LayerGeometryProvider *v, int y) const { - float min = 0.0, max = 0.0; + double min = 0.0, max = 0.0; bool logarithmic = false; - int h = v->height(); + int h = v->getPaintHeight(); getScaleExtents(v, min, max, logarithmic); - float val = min + (float(h - y) * float(max - min)) / h; + double val = min + (double(h - y) * double(max - min)) / h; if (logarithmic) { val = LogRange::map(val); @@ -857,9 +877,9 @@ } QColor -TimeValueLayer::getColourForValue(View *v, float val) const +TimeValueLayer::getColourForValue(LayerGeometryProvider *v, double val) const { - float min, max; + double min, max; bool log; getScaleExtents(v, min, max, log); @@ -888,11 +908,11 @@ } void -TimeValueLayer::paint(View *v, QPainter &paint, QRect rect) const +TimeValueLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) return; - int sampleRate = m_model->getSampleRate(); + sv_samplerate_t sampleRate = m_model->getSampleRate(); if (!sampleRate) return; paint.setRenderHint(QPainter::Antialiasing, false); @@ -900,8 +920,8 @@ // Profiler profiler("TimeValueLayer::paint", true); int x0 = rect.left(), x1 = rect.right(); - long frame0 = v->getFrameForX(x0); - long frame1 = v->getFrameForX(x1); + sv_frame_t frame0 = v->getFrameForX(x0); + sv_frame_t frame1 = v->getFrameForX(x1); if (m_derivative) --frame0; SparseTimeValueModel::PointList points(m_model->getPoints @@ -919,15 +939,15 @@ << m_model->getResolution() << " frames" << endl; #endif - float min = m_model->getValueMinimum(); - float max = m_model->getValueMaximum(); + double min = m_model->getValueMinimum(); + double max = m_model->getValueMaximum(); if (max == min) max = min + 1.0; - int origin = int(nearbyint(v->height() - - (-min * v->height()) / (max - min))); + int origin = int(nearbyint(v->getPaintHeight() - + (-min * v->getPaintHeight()) / (max - min))); QPoint localPos; - long illuminateFrame = -1; + sv_frame_t illuminateFrame = -1; if (v->shouldIlluminateLocalFeatures(this, localPos)) { SparseTimeValueModel::PointList localPoints = @@ -958,7 +978,7 @@ textY = v->getTextLabelHeight(this, paint); } else { int originY = getYForValue(v, 0.f); - if (originY > 0 && originY < v->height()) { + if (originY > 0 && originY < v->getPaintHeight()) { paint.save(); paint.setPen(getPartialShades(v)[1]); paint.drawLine(x0, originY, x1, originY); @@ -966,16 +986,16 @@ } } - int prevFrame = 0; + sv_frame_t prevFrame = 0; for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { if (m_derivative && i == points.begin()) continue; - + const SparseTimeValueModel::Point &p(*i); - float value = p.value; + double value = p.value; if (m_derivative) { SparseTimeValueModel::PointList::const_iterator j = i; --j; @@ -987,6 +1007,10 @@ bool gap = false; if (m_plotStyle == PlotDiscreteCurves) { + if (value == 0.0) { + // Treat zeros as gaps + continue; + } gap = (p.frame > prevFrame && (p.frame - prevFrame >= m_model->getResolution() * 2)); } @@ -1000,7 +1024,8 @@ } bool haveNext = false; - int nf = v->getModelsEndFrame(); + double nvalue = 0.f; + sv_frame_t nf = v->getModelsEndFrame(); int nx = v->getXForFrame(nf); int ny = y; @@ -1009,7 +1034,7 @@ if (j != points.end()) { const SparseTimeValueModel::Point &q(*j); - float nvalue = q.value; + nvalue = q.value; if (m_derivative) nvalue -= p.value; nf = q.frame; nx = v->getXForFrame(nf); @@ -1109,14 +1134,16 @@ } else { - float x0 = x + float(w)/2; - float x1 = nx + float(w)/2; + double x0 = x + double(w)/2; + double x1 = nx + double(w)/2; - float y0 = y; - float y1 = ny; + double y0 = y; + double y1 = ny; if (m_plotStyle == PlotDiscreteCurves) { - bool nextGap = nf - p.frame >= m_model->getResolution() * 2; + bool nextGap = + (nvalue == 0.0) || + (nf - p.frame >= m_model->getResolution() * 2); if (nextGap) { x1 = x0; y1 = y0; @@ -1136,6 +1163,7 @@ // path.quadTo(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2); } else { + path.lineTo(x0, y0); path.lineTo((x0 + x1) / 2, (y0 + y1) / 2); } } @@ -1155,39 +1183,45 @@ if (!illuminate) { if (!m_drawSegmentDivisions || nx < x + 5 || - x >= v->width() - 1) { + x >= v->getPaintWidth() - 1) { paint.setPen(Qt::NoPen); } } - paint.drawRect(x, -1, nx - x, v->height() + 1); + paint.drawRect(x, -1, nx - x, v->getPaintHeight() + 1); } - QString label = p.label; - bool italic = false; + if (v->shouldShowFeatureLabels()) { - if (label == "" && - (m_plotStyle == PlotPoints || - m_plotStyle == PlotSegmentation || - m_plotStyle == PlotConnectedPoints)) { - char lc[20]; - snprintf(lc, 20, "%.3g", p.value); - label = lc; - italic = true; + QString label = p.label; + bool italic = false; + + if (label == "" && + (m_plotStyle == PlotPoints || + m_plotStyle == PlotSegmentation || + m_plotStyle == PlotConnectedPoints)) { + char lc[20]; + snprintf(lc, 20, "%.3g", p.value); + label = lc; + italic = true; + } + + if (label != "") { + // Quick test for 20px before we do the slower test using metrics + bool haveRoom = (nx > x + 20); + haveRoom = (haveRoom && + (nx > x + 6 + paint.fontMetrics().width(label))); + if (haveRoom || + (!haveNext && + (pointCount == 0 || !italic))) { + v->drawVisibleText(paint, x + 5, textY, label, + italic ? + View::OutlinedItalicText : + View::OutlinedText); + } + } } - if (label != "") { - bool haveRoom = nx > x + 6 + paint.fontMetrics().width(label); - if (haveRoom || - (!haveNext && - (pointCount == 0 || !italic))) { - v->drawVisibleText(paint, x + 5, textY, label, - italic ? - View::OutlinedItalicText : - View::OutlinedText); - } - } - prevFrame = p.frame; ++pointCount; } @@ -1197,7 +1231,7 @@ paint.drawPath(path); } else if ((m_plotStyle == PlotCurve || m_plotStyle == PlotLines) && !path.isEmpty()) { - paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->width()); + paint.setRenderHint(QPainter::Antialiasing, pointCount <= v->getPaintWidth()); paint.drawPath(path); } @@ -1208,7 +1242,7 @@ } int -TimeValueLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const +TimeValueLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const { if (!m_model || shouldAutoAlign()) { return 0; @@ -1228,16 +1262,16 @@ } void -TimeValueLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const +TimeValueLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const { - if (!m_model) return; + if (!m_model || m_model->getPoints().empty()) return; QString unit; - float min, max; + double min, max; bool logarithmic; int w = getVerticalScaleWidth(v, false, paint); - int h = v->height(); + int h = v->getPaintHeight(); if (m_plotStyle == PlotSegmentation) { @@ -1280,7 +1314,7 @@ } void -TimeValueLayer::drawStart(View *v, QMouseEvent *e) +TimeValueLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) { #ifdef DEBUG_TIME_VALUE_LAYER cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; @@ -1288,12 +1322,12 @@ if (!m_model) return; - long frame = v->getFrameForX(e->x()); - long resolution = m_model->getResolution(); + sv_frame_t frame = v->getFrameForX(e->x()); + int resolution = m_model->getResolution(); if (frame < 0) frame = 0; frame = (frame / resolution) * resolution; - float value = getValueForY(v, e->y()); + double value = getValueForY(v, e->y()); bool havePoint = false; @@ -1314,7 +1348,7 @@ if (!havePoint) { m_editingPoint = SparseTimeValueModel::Point - (frame, value, tr("New Point")); + (frame, float(value), tr("New Point")); } m_originalPoint = m_editingPoint; @@ -1330,7 +1364,7 @@ } void -TimeValueLayer::drawDrag(View *v, QMouseEvent *e) +TimeValueLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) { #ifdef DEBUG_TIME_VALUE_LAYER cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; @@ -1338,12 +1372,12 @@ if (!m_model || !m_editing) return; - long frame = v->getFrameForX(e->x()); - long resolution = m_model->getResolution(); + sv_frame_t frame = v->getFrameForX(e->x()); + int resolution = m_model->getResolution(); if (frame < 0) frame = 0; frame = (frame / resolution) * resolution; - float value = getValueForY(v, e->y()); + double value = getValueForY(v, e->y()); SparseTimeValueModel::PointList points = getLocalPoints(v, e->x()); @@ -1387,12 +1421,12 @@ // m_editingCommand->deletePoint(m_editingPoint); m_editingPoint.frame = frame; - m_editingPoint.value = value; + m_editingPoint.value = float(value); m_editingCommand->addPoint(m_editingPoint); } void -TimeValueLayer::drawEnd(View *, QMouseEvent *) +TimeValueLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *) { #ifdef DEBUG_TIME_VALUE_LAYER cerr << "TimeValueLayer::drawEnd" << endl; @@ -1404,7 +1438,7 @@ } void -TimeValueLayer::eraseStart(View *v, QMouseEvent *e) +TimeValueLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return; @@ -1422,12 +1456,12 @@ } void -TimeValueLayer::eraseDrag(View *v, QMouseEvent *e) +TimeValueLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *) { } void -TimeValueLayer::eraseEnd(View *v, QMouseEvent *e) +TimeValueLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; @@ -1449,7 +1483,7 @@ } void -TimeValueLayer::editStart(View *v, QMouseEvent *e) +TimeValueLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) { #ifdef DEBUG_TIME_VALUE_LAYER cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; @@ -1472,7 +1506,7 @@ } void -TimeValueLayer::editDrag(View *v, QMouseEvent *e) +TimeValueLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) { #ifdef DEBUG_TIME_VALUE_LAYER cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl; @@ -1480,11 +1514,11 @@ if (!m_model || !m_editing) return; - long frame = v->getFrameForX(e->x()); + sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); - float value = getValueForY(v, e->y()); + double value = getValueForY(v, e->y()); if (!m_editingCommand) { m_editingCommand = new SparseTimeValueModel::EditCommand(m_model, @@ -1493,12 +1527,12 @@ m_editingCommand->deletePoint(m_editingPoint); m_editingPoint.frame = frame; - m_editingPoint.value = value; + m_editingPoint.value = float(value); m_editingCommand->addPoint(m_editingPoint); } void -TimeValueLayer::editEnd(View *, QMouseEvent *) +TimeValueLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) { #ifdef DEBUG_TIME_VALUE_LAYER cerr << "TimeValueLayer::editEnd" << endl; @@ -1528,7 +1562,7 @@ } bool -TimeValueLayer::editOpen(View *v, QMouseEvent *e) +TimeValueLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return false; @@ -1567,7 +1601,7 @@ } void -TimeValueLayer::moveSelection(Selection s, size_t newStartFrame) +TimeValueLayer::moveSelection(Selection s, sv_frame_t newStartFrame) { if (!m_model) return; @@ -1613,9 +1647,9 @@ if (s.contains(i->frame)) { - double target = i->frame; - target = newSize.getStartFrame() + - double(target - s.getStartFrame()) * ratio; + double target = double(i->frame); + target = double(newSize.getStartFrame()) + + target - double(s.getStartFrame()) * ratio; SparseTimeValueModel::Point newPoint(*i); newPoint.frame = lrint(target); @@ -1651,7 +1685,7 @@ } void -TimeValueLayer::copy(View *v, Selection s, Clipboard &to) +TimeValueLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) { if (!m_model) return; @@ -1669,7 +1703,7 @@ } bool -TimeValueLayer::paste(View *v, const Clipboard &from, int frameOffset, +TimeValueLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool interactive) { if (!m_model) return false; @@ -1681,7 +1715,7 @@ if (clipboardHasDifferentAlignment(v, from)) { QMessageBox::StandardButton button = - QMessageBox::question(v, tr("Re-align pasted items?"), + QMessageBox::question(v->getView(), tr("Re-align pasted items?"), tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); @@ -1708,7 +1742,6 @@ Labeller::ValueType generation = Labeller::ValueNone; bool haveUsableLabels = false; - bool haveExistingItems = !(m_model->isEmpty()); Labeller labeller; labeller.setSampleRate(m_model->getSampleRate()); @@ -1776,7 +1809,10 @@ (0, tr("Choose value calculation"), text, options, prevSelection, &ok); - if (!ok) return false; + if (!ok) { + delete command; + return false; + } int selection = 0; generation = Labeller::ValueNone; @@ -1810,7 +1846,7 @@ if (!i->haveFrame()) continue; - size_t frame = 0; + sv_frame_t frame = 0; if (!realign) { diff -r 282f4be8f058 -r c02c51ae5238 layer/TimeValueLayer.h --- a/layer/TimeValueLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/TimeValueLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -37,41 +37,41 @@ public: TimeValueLayer(); - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const; - virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const; + virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const; + virtual void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; - virtual QString getLabelPreceding(size_t) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; + virtual QString getLabelPreceding(sv_frame_t) const; - virtual bool snapToFeatureFrame(View *v, int &frame, - size_t &resolution, + virtual bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual bool snapToSimilarFeature(View *v, int &frame, - size_t &resolution, + virtual bool snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, + int &resolution, SnapType snap) const; - virtual void drawStart(View *v, QMouseEvent *); - virtual void drawDrag(View *v, QMouseEvent *); - virtual void drawEnd(View *v, QMouseEvent *); + virtual void drawStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void drawEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void eraseStart(View *v, QMouseEvent *); - virtual void eraseDrag(View *v, QMouseEvent *); - virtual void eraseEnd(View *v, QMouseEvent *); + virtual void eraseStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void eraseEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual void editStart(View *v, QMouseEvent *); - virtual void editDrag(View *v, QMouseEvent *); - virtual void editEnd(View *v, QMouseEvent *); + virtual void editStart(LayerGeometryProvider *v, QMouseEvent *); + virtual void editDrag(LayerGeometryProvider *v, QMouseEvent *); + virtual void editEnd(LayerGeometryProvider *v, QMouseEvent *); - virtual bool editOpen(View *v, QMouseEvent *); + virtual bool editOpen(LayerGeometryProvider *v, QMouseEvent *); - virtual void moveSelection(Selection s, size_t newStartFrame); + virtual void moveSelection(Selection s, sv_frame_t newStartFrame); virtual void resizeSelection(Selection s, Selection newSize); virtual void deleteSelection(Selection s); - virtual void copy(View *v, Selection s, Clipboard &to); - virtual bool paste(View *v, const Clipboard &from, int frameOffset, + virtual void copy(LayerGeometryProvider *v, Selection s, Clipboard &to); + virtual bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive); virtual const Model *getModel() const { return m_model; } @@ -120,21 +120,21 @@ void setShowDerivative(bool); bool getShowDerivative() const { return m_derivative; } - virtual bool isLayerScrollable(const View *v) const; + virtual bool isLayerScrollable(const LayerGeometryProvider *v) const; virtual bool isLayerEditable() const { return true; } - virtual int getCompletion(View *) const { return m_model->getCompletion(); } + virtual int getCompletion(LayerGeometryProvider *) const { return m_model->getCompletion(); } virtual bool needsTextLabelHeight() const { return m_plotStyle == PlotSegmentation && m_model->hasTextLabels(); } - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const; - virtual bool getDisplayExtents(float &min, float &max) const; - virtual bool setDisplayExtents(float min, float max); + virtual bool getDisplayExtents(double &min, double &max) const; + virtual bool setDisplayExtents(double min, double max); virtual int getVerticalZoomSteps(int &defaultStep) const; virtual int getCurrentVerticalZoomStep() const; @@ -155,16 +155,16 @@ } /// VerticalScaleLayer and ColourScaleLayer methods - virtual int getYForValue(View *, float value) const; - virtual float getValueForY(View *, int y) const; + virtual int getYForValue(LayerGeometryProvider *, double value) const; + virtual double getValueForY(LayerGeometryProvider *, int y) const; virtual QString getScaleUnits() const; - virtual QColor getColourForValue(View *v, float value) const; + virtual QColor getColourForValue(LayerGeometryProvider *v, double value) const; protected: - void getScaleExtents(View *, float &min, float &max, bool &log) const; + void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const; bool shouldAutoAlign() const; - SparseTimeValueModel::PointList getLocalPoints(View *v, int) const; + SparseTimeValueModel::PointList getLocalPoints(LayerGeometryProvider *v, int) const; virtual int getDefaultColourHint(bool dark, bool &impose); @@ -179,8 +179,8 @@ bool m_drawSegmentDivisions; bool m_derivative; - mutable float m_scaleMinimum; - mutable float m_scaleMaximum; + mutable double m_scaleMinimum; + mutable double m_scaleMaximum; void finish(SparseTimeValueModel::EditCommand *command) { Command *c = command->finish(); diff -r 282f4be8f058 -r c02c51ae5238 layer/VerticalScaleLayer.h --- a/layer/VerticalScaleLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/VerticalScaleLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -19,8 +19,8 @@ class VerticalScaleLayer { public: - virtual int getYForValue(View *, float value) const = 0; - virtual float getValueForY(View *, int y) const = 0; + virtual int getYForValue(LayerGeometryProvider *, double value) const = 0; + virtual double getValueForY(LayerGeometryProvider *, int y) const = 0; virtual QString getScaleUnits() const = 0; }; diff -r 282f4be8f058 -r c02c51ae5238 layer/WaveformLayer.cpp --- a/layer/WaveformLayer.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/WaveformLayer.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -43,9 +43,11 @@ m_channelMode(SeparateChannels), m_channel(-1), m_scale(LinearScale), + m_middleLineHeight(0.5), m_aggressive(false), m_cache(0), - m_cacheValid(false) + m_cacheValid(false), + m_cacheZoomLevel(0) { } @@ -151,7 +153,7 @@ *max = 50; *deflt = 0; - val = lrint(log10(m_gain) * 20.0); + val = int(lrint(log10(m_gain) * 20.0)); if (val < *min) val = *min; if (val > *max) val = *max; @@ -220,7 +222,7 @@ WaveformLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(pow(10, float(value)/20.0)); + setGain(float(pow(10, float(value)/20.0))); } else if (name == "Normalize Visible Area") { setAutoNormalize(value ? true : false); } else if (name == "Channels") { @@ -306,6 +308,15 @@ } void +WaveformLayer::setMiddleLineHeight(double height) +{ + if (m_middleLineHeight == height) return; + m_middleLineHeight = height; + m_cacheValid = false; + emit layerParametersChanged(); +} + +void WaveformLayer::setAggressiveCacheing(bool aggressive) { if (m_aggressive == aggressive) return; @@ -315,7 +326,7 @@ } int -WaveformLayer::getCompletion(View *) const +WaveformLayer::getCompletion(LayerGeometryProvider *) const { int completion = 100; if (!m_model || !m_model->isOK()) return completion; @@ -324,7 +335,7 @@ } bool -WaveformLayer::getValueExtents(float &min, float &max, +WaveformLayer::getValueExtents(double &min, double &max, bool &, QString &unit) const { if (m_scale == LinearScale) { @@ -342,26 +353,26 @@ } int -WaveformLayer::dBscale(float sample, int m) const +WaveformLayer::dBscale(double sample, int m) const { if (sample < 0.0) return dBscale(-sample, m); - float dB = AudioLevel::multiplier_to_dB(sample); + double dB = AudioLevel::multiplier_to_dB(sample); if (dB < -50.0) return 0; if (dB > 0.0) return m; return int(((dB + 50.0) * m) / 50.0 + 0.1); } -size_t -WaveformLayer::getChannelArrangement(size_t &min, size_t &max, +int +WaveformLayer::getChannelArrangement(int &min, int &max, bool &merging, bool &mixing) const { if (!m_model || !m_model->isOK()) return 0; - size_t channels = m_model->getChannelCount(); + int channels = m_model->getChannelCount(); if (channels == 0) return 0; - size_t rawChannels = channels; + int rawChannels = channels; if (m_channel == -1) { min = 0; @@ -388,7 +399,7 @@ } bool -WaveformLayer::isLayerScrollable(const View *) const +WaveformLayer::isLayerScrollable(const LayerGeometryProvider *) const { return !m_autoNormalize; } @@ -397,10 +408,10 @@ -5, -3, -2, -1, -0.5, 0 }; bool -WaveformLayer::getSourceFramesForX(View *v, int x, size_t modelZoomLevel, - size_t &f0, size_t &f1) const +WaveformLayer::getSourceFramesForX(LayerGeometryProvider *v, int x, int modelZoomLevel, + sv_frame_t &f0, sv_frame_t &f1) const { - long viewFrame = v->getFrameForX(x); + sv_frame_t viewFrame = v->getFrameForX(x); if (viewFrame < 0) { f0 = 0; f1 = 0; @@ -422,17 +433,15 @@ } float -WaveformLayer::getNormalizeGain(View *v, int channel) const +WaveformLayer::getNormalizeGain(LayerGeometryProvider *v, int channel) const { - long startFrame = v->getStartFrame(); - long endFrame = v->getEndFrame(); + sv_frame_t startFrame = v->getStartFrame(); + sv_frame_t endFrame = v->getEndFrame(); - // Although a long for purposes of comparison against the view - // start and end frames, these are known to be non-negative - long modelStart = long(m_model->getStartFrame()); - long modelEnd = long(m_model->getEndFrame()); + sv_frame_t modelStart = m_model->getStartFrame(); + sv_frame_t modelEnd = m_model->getEndFrame(); - size_t rangeStart, rangeEnd; + sv_frame_t rangeStart, rangeEnd; if (startFrame < modelStart) rangeStart = modelStart; else rangeStart = startFrame; @@ -446,11 +455,11 @@ RangeSummarisableTimeValueModel::Range range = m_model->getSummary(channel, rangeStart, rangeEnd - rangeStart); - size_t minChannel = 0, maxChannel = 0; + int minChannel = 0, maxChannel = 0; bool mergingChannels = false, mixingChannels = false; - getChannelArrangement(minChannel, maxChannel, - mergingChannels, mixingChannels); + (void)getChannelArrangement(minChannel, maxChannel, + mergingChannels, mixingChannels); if (mergingChannels || mixingChannels) { RangeSummarisableTimeValueModel::Range otherRange = @@ -460,11 +469,11 @@ range.setAbsmean(std::min(range.absmean(), otherRange.absmean())); } - return 1.0 / std::max(fabsf(range.max()), fabsf(range.min())); + return float(1.0 / std::max(fabs(range.max()), fabs(range.min()))); } void -WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const +WaveformLayer::paint(LayerGeometryProvider *v, QPainter &viewPainter, QRect rect) const { if (!m_model || !m_model->isOK()) { return; @@ -478,15 +487,15 @@ << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << endl; #endif - size_t channels = 0, minChannel = 0, maxChannel = 0; + int channels = 0, minChannel = 0, maxChannel = 0; bool mergingChannels = false, mixingChannels = false; channels = getChannelArrangement(minChannel, maxChannel, mergingChannels, mixingChannels); if (channels == 0) return; - int w = v->width(); - int h = v->height(); + int w = v->getPaintWidth(); + int h = v->getPaintHeight(); bool ready = m_model->isReady(); QPainter *paint; @@ -532,6 +541,15 @@ paint->setRenderHint(QPainter::Antialiasing, false); + if (m_middleLineHeight != 0.5) { + paint->save(); + double space = m_middleLineHeight * 2; + if (space > 1.0) space = 2.0 - space; + double yt = h * (m_middleLineHeight - space/2); + paint->translate(QPointF(0, yt)); + paint->scale(1.0, space); + } + int x0 = 0, x1 = w - 1; int y0 = 0, y1 = h - 1; @@ -541,7 +559,7 @@ y1 = rect.bottom(); if (x0 > 0) --x0; - if (x1 < v->width()) ++x1; + if (x1 < w) ++x1; // Our zoom level may differ from that at which the underlying // model has its blocks. @@ -552,11 +570,11 @@ // must remain the same when we scroll one or more pixels left or // right. - size_t modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel); + int modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel); - size_t frame0; - size_t frame1; - size_t spare; + sv_frame_t frame0; + sv_frame_t frame1; + sv_frame_t spare; getSourceFramesForX(v, x0, modelZoomLevel, frame0, spare); getSourceFramesForX(v, x1, modelZoomLevel, spare, frame1); @@ -583,11 +601,11 @@ midColour = midColour.light(50); } - while (m_effectiveGains.size() <= maxChannel) { + while ((int)m_effectiveGains.size() <= maxChannel) { m_effectiveGains.push_back(m_gain); } - for (size_t ch = minChannel; ch <= maxChannel; ++ch) { + for (int ch = minChannel; ch <= maxChannel; ++ch) { int prevRangeBottom = -1, prevRangeTop = -1; QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour; @@ -598,7 +616,7 @@ m_effectiveGains[ch] = getNormalizeGain(v, ch); } - float gain = m_effectiveGains[ch]; + double gain = m_effectiveGains[ch]; int m = (h / channels) / 2; int my = m + (((ch - minChannel) * h) / channels); @@ -629,7 +647,7 @@ for (int i = 1; i < n; ++i) { - float val = 0.0, nval = 0.0; + double val = 0.0, nval = 0.0; switch (m_scale) { @@ -692,7 +710,7 @@ range = RangeSummarisableTimeValueModel::Range(); - size_t f0, f1; + sv_frame_t f0, f1; if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) continue; f1 = f1 - 1; @@ -701,8 +719,8 @@ continue; } - size_t i0 = (f0 - frame0) / modelZoomLevel; - size_t i1 = (f1 - frame0) / modelZoomLevel; + sv_frame_t i0 = (f0 - frame0) / modelZoomLevel; + sv_frame_t i1 = (f1 - frame0) / modelZoomLevel; #ifdef DEBUG_WAVEFORM_PAINT cerr << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << endl; @@ -712,14 +730,17 @@ cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << zoomLevel << ", model zoom = " << modelZoomLevel << ")" << endl; } - if (ranges && i0 < ranges->size()) { + if (ranges && i0 < (sv_frame_t)ranges->size()) { - range = (*ranges)[i0]; + range = (*ranges)[size_t(i0)]; - if (i1 > i0 && i1 < ranges->size()) { - range.setMax(std::max(range.max(), (*ranges)[i1].max())); - range.setMin(std::min(range.min(), (*ranges)[i1].min())); - range.setAbsmean((range.absmean() + (*ranges)[i1].absmean()) / 2); + if (i1 > i0 && i1 < (int)ranges->size()) { + range.setMax(std::max(range.max(), + (*ranges)[size_t(i1)].max())); + range.setMin(std::min(range.min(), + (*ranges)[size_t(i1)].min())); + range.setAbsmean((range.absmean() + + (*ranges)[size_t(i1)].absmean()) / 2); } } else { @@ -733,30 +754,33 @@ if (mergingChannels) { - if (otherChannelRanges && i0 < otherChannelRanges->size()) { + if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) { range.setMax(fabsf(range.max())); - range.setMin(-fabsf((*otherChannelRanges)[i0].max())); + range.setMin(-fabsf((*otherChannelRanges)[size_t(i0)].max())); range.setAbsmean ((range.absmean() + - (*otherChannelRanges)[i0].absmean()) / 2); + (*otherChannelRanges)[size_t(i0)].absmean()) / 2); - if (i1 > i0 && i1 < otherChannelRanges->size()) { + if (i1 > i0 && i1 < (sv_frame_t)otherChannelRanges->size()) { // let's not concern ourselves about the mean range.setMin (std::min (range.min(), - -fabsf((*otherChannelRanges)[i1].max()))); + -fabsf((*otherChannelRanges)[size_t(i1)].max()))); } } } else if (mixingChannels) { - if (otherChannelRanges && i0 < otherChannelRanges->size()) { + if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) { - range.setMax((range.max() + (*otherChannelRanges)[i0].max()) / 2); - range.setMin((range.min() + (*otherChannelRanges)[i0].min()) / 2); - range.setAbsmean((range.absmean() + (*otherChannelRanges)[i0].absmean()) / 2); + range.setMax((range.max() + + (*otherChannelRanges)[size_t(i0)].max()) / 2); + range.setMin((range.min() + + (*otherChannelRanges)[size_t(i0)].min()) / 2); + range.setAbsmean((range.absmean() + + (*otherChannelRanges)[size_t(i0)].absmean()) / 2); } } @@ -766,10 +790,10 @@ switch (m_scale) { case LinearScale: - rangeBottom = int( m * greyLevels * range.min() * gain); - rangeTop = int( m * greyLevels * range.max() * gain); - meanBottom = int(-m * range.absmean() * gain); - meanTop = int( m * range.absmean() * gain); + rangeBottom = int(double(m * greyLevels) * range.min() * gain); + rangeTop = int(double(m * greyLevels) * range.max() * gain); + meanBottom = int(double(-m) * range.absmean() * gain); + meanTop = int(double(m) * range.absmean() * gain); break; case dBScale: @@ -915,9 +939,13 @@ } } + if (m_middleLineHeight != 0.5) { + paint->restore(); + } + if (m_aggressive) { - if (ready && rect == v->rect()) { + if (ready && rect == v->getPaintRect()) { m_cacheValid = true; m_cacheZoomLevel = zoomLevel; } @@ -931,7 +959,7 @@ } QString -WaveformLayer::getFeatureDescription(View *v, QPoint &pos) const +WaveformLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); @@ -939,9 +967,9 @@ int zoomLevel = v->getZoomLevel(); - size_t modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel); + int modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel); - size_t f0, f1; + sv_frame_t f0, f1; if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) return ""; QString text; @@ -958,16 +986,16 @@ .arg(rt0.toText(true).c_str()); } - size_t channels = 0, minChannel = 0, maxChannel = 0; + int channels = 0, minChannel = 0, maxChannel = 0; bool mergingChannels = false, mixingChannels = false; channels = getChannelArrangement(minChannel, maxChannel, mergingChannels, mixingChannels); if (channels == 0) return ""; - for (size_t ch = minChannel; ch <= maxChannel; ++ch) { + for (int ch = minChannel; ch <= maxChannel; ++ch) { - size_t blockSize = v->getZoomLevel(); + int blockSize = v->getZoomLevel(); RangeSummarisableTimeValueModel::RangeBlock ranges; m_model->getSummaries(ch, f0, f1 - f0, ranges, blockSize); @@ -983,18 +1011,18 @@ } bool singleValue = false; - float min, max; + double min, max; if (fabs(range.min()) < 0.01) { min = range.min(); max = range.max(); singleValue = (min == max); } else { - int imin = lrint(range.min() * 10000); - int imax = lrint(range.max() * 10000); + int imin = int(lrint(range.min() * 10000)); + int imax = int(lrint(range.max() * 10000)); singleValue = (imin == imax); - min = float(imin)/10000; - max = float(imax)/10000; + min = double(imin)/10000; + max = double(imax)/10000; } int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()), @@ -1003,10 +1031,10 @@ if (!singleValue) { text += tr("\n%1\t%2 - %3 (%4 dB peak)") - .arg(label).arg(min).arg(max).arg(float(db)/100); + .arg(label).arg(min).arg(max).arg(double(db)/100); } else { text += tr("\n%1\t%2 (%3 dB peak)") - .arg(label).arg(min).arg(float(db)/100); + .arg(label).arg(min).arg(double(db)/100); } } @@ -1014,17 +1042,17 @@ } int -WaveformLayer::getYForValue(const View *v, float value, size_t channel) const +WaveformLayer::getYForValue(const LayerGeometryProvider *v, double value, int channel) const { - size_t channels = 0, minChannel = 0, maxChannel = 0; + int channels = 0, minChannel = 0, maxChannel = 0; bool mergingChannels = false, mixingChannels = false; channels = getChannelArrangement(minChannel, maxChannel, mergingChannels, mixingChannels); - + if (channels == 0) return 0; if (maxChannel < minChannel || channel < minChannel) return 0; - int h = v->height(); + int h = v->getPaintHeight(); int m = (h / channels) / 2; if ((m_scale == dBScale || m_scale == MeterScale) && @@ -1056,18 +1084,18 @@ return my - vy; } -float -WaveformLayer::getValueForY(const View *v, int y, size_t &channel) const +double +WaveformLayer::getValueForY(const LayerGeometryProvider *v, int y, int &channel) const { - size_t channels = 0, minChannel = 0, maxChannel = 0; + int channels = 0, minChannel = 0, maxChannel = 0; bool mergingChannels = false, mixingChannels = false; channels = getChannelArrangement(minChannel, maxChannel, mergingChannels, mixingChannels); - + if (channels == 0) return 0; if (maxChannel < minChannel) return 0; - int h = v->height(); + int h = v->getPaintHeight(); int m = (h / channels) / 2; if ((m_scale == dBScale || m_scale == MeterScale) && @@ -1080,13 +1108,13 @@ int my = m + (((channel - minChannel) * h) / channels); int vy = my - y; - float value = 0; - float thresh = -50.f; + double value = 0; + double thresh = -50.f; switch (m_scale) { case LinearScale: - value = float(vy) / m; + value = double(vy) / m; break; case MeterScale: @@ -1094,7 +1122,7 @@ break; case dBScale: - value = (-thresh * float(vy)) / m + thresh; + value = (-thresh * double(vy)) / m + thresh; value = AudioLevel::dB_to_multiplier(value); break; } @@ -1103,19 +1131,19 @@ } bool -WaveformLayer::getYScaleValue(const View *v, int y, - float &value, QString &unit) const +WaveformLayer::getYScaleValue(const LayerGeometryProvider *v, int y, + double &value, QString &unit) const { - size_t channel; + int channel; value = getValueForY(v, y, channel); if (m_scale == dBScale || m_scale == MeterScale) { - float thresh = -50.f; + double thresh = -50.f; - if (value > 0.f) { - value = 10.f * log10f(value); + if (value > 0.0) { + value = 10.0 * log10(value); if (value < thresh) value = thresh; } else value = thresh; @@ -1129,37 +1157,37 @@ } bool -WaveformLayer::getYScaleDifference(const View *v, int y0, int y1, - float &diff, QString &unit) const +WaveformLayer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1, + double &diff, QString &unit) const { - size_t c0, c1; - float v0 = getValueForY(v, y0, c0); - float v1 = getValueForY(v, y1, c1); + int c0, c1; + double v0 = getValueForY(v, y0, c0); + double v1 = getValueForY(v, y1, c1); if (c0 != c1) { // different channels, not comparable - diff = 0.f; + diff = 0.0; unit = ""; return false; } if (m_scale == dBScale || m_scale == MeterScale) { - float thresh = -50.f; + double thresh = -50.0; if (v1 == v0) diff = thresh; else { if (v1 > v0) diff = v0 / v1; else diff = v1 / v0; - diff = 10.f * log10f(diff); + diff = 10.0 * log10(diff); if (diff < thresh) diff = thresh; } unit = "dBV"; } else { - diff = fabsf(v1 - v0); + diff = fabs(v1 - v0); unit = "V"; } @@ -1167,7 +1195,7 @@ } int -WaveformLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const +WaveformLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const { if (m_scale == LinearScale) { return paint.fontMetrics().width("0.0") + 13; @@ -1178,13 +1206,13 @@ } void -WaveformLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const +WaveformLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) { return; } - size_t channels = 0, minChannel = 0, maxChannel = 0; + int channels = 0, minChannel = 0, maxChannel = 0; bool mergingChannels = false, mixingChannels = false; channels = getChannelArrangement(minChannel, maxChannel, @@ -1195,26 +1223,26 @@ int textHeight = paint.fontMetrics().height(); int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1; - float gain = m_gain; + double gain = m_gain; - for (size_t ch = minChannel; ch <= maxChannel; ++ch) { + for (int ch = minChannel; ch <= maxChannel; ++ch) { int lastLabelledY = -1; - if (ch < m_effectiveGains.size()) gain = m_effectiveGains[ch]; + if (ch < (int)m_effectiveGains.size()) gain = m_effectiveGains[ch]; int n = 10; for (int i = 0; i <= n; ++i) { - float val = 0.0, nval = 0.0; + double val = 0.0, nval = 0.0; QString text = ""; switch (m_scale) { case LinearScale: val = (i * gain) / n; - text = QString("%1").arg(float(i) / n); + text = QString("%1").arg(double(i) / n); if (i == 0) text = "0.0"; else { nval = -val; @@ -1314,14 +1342,16 @@ "channelMode=\"%4\" " "channel=\"%5\" " "scale=\"%6\" " - "aggressive=\"%7\" " - "autoNormalize=\"%8\"") + "middleLineHeight=\"%7\" " + "aggressive=\"%8\" " + "autoNormalize=\"%9\"") .arg(m_gain) .arg(m_showMeans) .arg(m_greyscale) .arg(m_channelMode) .arg(m_channel) .arg(m_scale) + .arg(m_middleLineHeight) .arg(m_aggressive) .arg(m_autoNormalize); @@ -1353,10 +1383,12 @@ int channel = attributes.value("channel").toInt(&ok); if (ok) setChannel(channel); - Scale scale = (Scale) - attributes.value("scale").toInt(&ok); + Scale scale = (Scale)attributes.value("scale").toInt(&ok); if (ok) setScale(scale); + float middleLineHeight = attributes.value("middleLineHeight").toFloat(&ok); + if (ok) setMiddleLineHeight(middleLineHeight); + bool aggressive = (attributes.value("aggressive") == "1" || attributes.value("aggressive") == "true"); setUseGreyscale(aggressive); @@ -1376,7 +1408,7 @@ int WaveformLayer::getCurrentVerticalZoomStep() const { - int val = lrint(log10(m_gain) * 20.0) + 50; + int val = int(lrint(log10(m_gain) * 20.0) + 50); if (val < 0) val = 0; if (val > 100) val = 100; return val; @@ -1385,6 +1417,6 @@ void WaveformLayer::setVerticalZoomStep(int step) { - setGain(pow(10, float(step - 50) / 20.0)); + setGain(powf(10, float(step - 50) / 20.f)); } diff -r 282f4be8f058 -r c02c51ae5238 layer/WaveformLayer.h --- a/layer/WaveformLayer.h Tue Jul 14 15:04:46 2015 +0100 +++ b/layer/WaveformLayer.h Wed Apr 20 12:06:28 2016 +0100 @@ -38,16 +38,16 @@ return m_model ? m_model->getZoomConstraint() : 0; } virtual const Model *getModel() const { return m_model; } - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const; - virtual QString getFeatureDescription(View *v, QPoint &) const; + virtual QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const; virtual ColourSignificance getLayerColourSignificance() const { return ColourAndBackgroundSignificant; } - virtual int getVerticalScaleWidth(View *v, bool detailed, QPainter &) const; - virtual void paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const; + virtual int getVerticalScaleWidth(LayerGeometryProvider *v, bool detailed, QPainter &) const; + virtual void paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const; void setModel(const RangeSummarisableTimeValueModel *model); @@ -147,6 +147,19 @@ Scale getScale() const { return m_scale; } /** + * Specify the height of the middle of the waveform track or + * tracks within the layer, from 0.0 to 1.0. + * + * A value of 0.0 would indicate that the waveform occupies + * effectively no space at the very top of the layer; 1.0 would + * indicate that the waveform occupies no space at the very + * bottom; the default value of 0.5 indicates that it occupies the + * whole layer, centred at the middle. + */ + void setMiddleLineHeight(double); + double getMiddleLineHeight() const { return m_middleLineHeight; } + + /** * Enable or disable aggressive pixmap cacheing. If enabled, * waveforms will be rendered to an off-screen pixmap and * refreshed from there instead of being redrawn from the peak @@ -167,18 +180,18 @@ void setAggressiveCacheing(bool); bool getAggressiveCacheing() const { return m_aggressive; } - virtual bool isLayerScrollable(const View *) const; + virtual bool isLayerScrollable(const LayerGeometryProvider *) const; - virtual int getCompletion(View *) const; + virtual int getCompletion(LayerGeometryProvider *) const; - virtual bool getValueExtents(float &min, float &max, + virtual bool getValueExtents(double &min, double &max, bool &log, QString &unit) const; - virtual bool getYScaleValue(const View *v, int y, - float &value, QString &unit) const; + virtual bool getYScaleValue(const LayerGeometryProvider *v, int y, + double &value, QString &unit) const; - virtual bool getYScaleDifference(const View *v, int y0, int y1, - float &diff, QString &unit) const; + virtual bool getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1, + double &diff, QString &unit) const; virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; @@ -189,23 +202,25 @@ virtual int getCurrentVerticalZoomStep() const; virtual void setVerticalZoomStep(int); + virtual bool canExistWithoutModel() const { return true; } + protected: - int dBscale(float sample, int m) const; + int dBscale(double sample, int m) const; const RangeSummarisableTimeValueModel *m_model; // I do not own this /// Return value is number of channels displayed - size_t getChannelArrangement(size_t &min, size_t &max, + int getChannelArrangement(int &min, int &max, bool &merging, bool &mixing) const; - int getYForValue(const View *v, float value, size_t channel) const; + int getYForValue(const LayerGeometryProvider *v, double value, int channel) const; - float getValueForY(const View *v, int y, size_t &channel) const; + double getValueForY(const LayerGeometryProvider *v, int y, int &channel) const; - bool getSourceFramesForX(View *v, int x, size_t modelZoomLevel, - size_t &f0, size_t &f1) const; + bool getSourceFramesForX(LayerGeometryProvider *v, int x, int modelZoomLevel, + sv_frame_t &f0, sv_frame_t &f1) const; - float getNormalizeGain(View *v, int channel) const; + float getNormalizeGain(LayerGeometryProvider *v, int channel) const; virtual void flagBaseColourChanged() { m_cacheValid = false; } @@ -216,6 +231,7 @@ ChannelMode m_channelMode; int m_channel; Scale m_scale; + double m_middleLineHeight; bool m_aggressive; mutable std::vector m_effectiveGains; diff -r 282f4be8f058 -r c02c51ae5238 layer/layer.pro --- a/layer/layer.pro Tue Jul 14 15:04:46 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -TEMPLATE = lib - -SV_UNIT_PACKAGES = fftw3f -load(../prf/sv.prf) - -CONFIG += sv staticlib qt thread warn_on stl rtti exceptions -QT += xml - -TARGET = svlayer - -DEPENDPATH += . .. -INCLUDEPATH += . .. -OBJECTS_DIR = tmp_obj -MOC_DIR = tmp_moc - -# Input -HEADERS += Colour3DPlotLayer.h \ - ColourDatabase.h \ - ColourMapper.h \ - ImageLayer.h \ - ImageRegionFinder.h \ - Layer.h \ - LayerFactory.h \ - NoteLayer.h \ - PaintAssistant.h \ - RegionLayer.h \ - SingleColourLayer.h \ - SliceableLayer.h \ - SliceLayer.h \ - SpectrogramLayer.h \ - SpectrumLayer.h \ - TextLayer.h \ - TimeInstantLayer.h \ - TimeRulerLayer.h \ - TimeValueLayer.h \ - WaveformLayer.h -SOURCES += Colour3DPlotLayer.cpp \ - ColourDatabase.cpp \ - ColourMapper.cpp \ - ImageLayer.cpp \ - ImageRegionFinder.cpp \ - Layer.cpp \ - LayerFactory.cpp \ - NoteLayer.cpp \ - PaintAssistant.cpp \ - RegionLayer.cpp \ - SingleColourLayer.cpp \ - SliceLayer.cpp \ - SpectrogramLayer.cpp \ - SpectrumLayer.cpp \ - TextLayer.cpp \ - TimeInstantLayer.cpp \ - TimeRulerLayer.cpp \ - TimeValueLayer.cpp \ - WaveformLayer.cpp diff -r 282f4be8f058 -r c02c51ae5238 svgui.pro --- a/svgui.pro Tue Jul 14 15:04:46 2015 +0100 +++ b/svgui.pro Wed Apr 20 12:06:28 2016 +0100 @@ -1,17 +1,35 @@ TEMPLATE = lib +INCLUDEPATH += ../vamp-plugin-sdk +DEFINES += HAVE_VAMP HAVE_VAMPHOSTSDK + exists(config.pri) { include(config.pri) } -win* { - !exists(config.pri) { - DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_VAMP HAVE_VAMPHOSTSDK HAVE_RUBBERBAND HAVE_DATAQUAY HAVE_LIBLO HAVE_MAD HAVE_ID3TAG +!exists(config.pri) { + + CONFIG += release + DEFINES += NDEBUG BUILD_RELEASE NO_TIMING + + win32-g++ { + INCLUDEPATH += ../sv-dependency-builds/win32-mingw/include + LIBS += -L../sv-dependency-builds/win32-mingw/lib } + win32-msvc* { + INCLUDEPATH += ../sv-dependency-builds/win32-msvc/include + LIBS += -L../sv-dependency-builds/win32-msvc/lib + } + macx* { + INCLUDEPATH += ../sv-dependency-builds/osx/include + LIBS += -L../sv-dependency-builds/osx/lib + } + + DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_RUBBERBAND HAVE_LIBLO HAVE_MAD HAVE_ID3TAG } -CONFIG += staticlib qt thread warn_on stl rtti exceptions -QT += network xml gui widgets +CONFIG += staticlib qt thread warn_on stl rtti exceptions c++11 +QT += network xml gui widgets svg TARGET = svgui @@ -20,17 +38,11 @@ OBJECTS_DIR = o MOC_DIR = o -win32-g++ { - INCLUDEPATH += ../sv-dependency-builds/win32-mingw/include -} -win32-msvc* { - INCLUDEPATH += ../sv-dependency-builds/win32-msvc/include -} - HEADERS += layer/Colour3DPlotLayer.h \ layer/ColourDatabase.h \ layer/ColourMapper.h \ layer/ColourScaleLayer.h \ + layer/FlexiNoteLayer.h \ layer/ImageLayer.h \ layer/ImageRegionFinder.h \ layer/Layer.h \ @@ -43,6 +55,7 @@ layer/PaintAssistant.h \ layer/PianoScale.h \ layer/RegionLayer.h \ + layer/ScrollableImageCache.h \ layer/SingleColourLayer.h \ layer/SliceableLayer.h \ layer/SliceLayer.h \ @@ -57,6 +70,7 @@ SOURCES += layer/Colour3DPlotLayer.cpp \ layer/ColourDatabase.cpp \ layer/ColourMapper.cpp \ + layer/FlexiNoteLayer.cpp \ layer/ImageLayer.cpp \ layer/ImageRegionFinder.cpp \ layer/Layer.cpp \ @@ -69,6 +83,7 @@ layer/PaintAssistant.cpp \ layer/PianoScale.cpp \ layer/RegionLayer.cpp \ + layer/ScrollableImageCache.cpp \ layer/SingleColourLayer.cpp \ layer/SliceLayer.cpp \ layer/SpectrogramLayer.cpp \ @@ -83,7 +98,8 @@ view/Pane.h \ view/PaneStack.h \ view/View.h \ - view/ViewManager.h + view/ViewManager.h \ + view/ViewProxy.h SOURCES += view/Overview.cpp \ view/Pane.cpp \ view/PaneStack.cpp \ @@ -106,6 +122,8 @@ widgets/LayerTree.h \ widgets/LayerTreeDialog.h \ widgets/LEDButton.h \ + widgets/LevelPanToolButton.h \ + widgets/LevelPanWidget.h \ widgets/ListInputDialog.h \ widgets/MIDIFileImportDialog.h \ widgets/ModelDataTableDialog.h \ @@ -126,6 +144,7 @@ widgets/Thumbwheel.h \ widgets/TipDialog.h \ widgets/TransformFinder.h \ + widgets/UnitConverter.h \ widgets/WindowShapePreview.h \ widgets/WindowTypeSelector.h SOURCES += widgets/ActivityLog.cpp \ @@ -143,6 +162,8 @@ widgets/LayerTree.cpp \ widgets/LayerTreeDialog.cpp \ widgets/LEDButton.cpp \ + widgets/LevelPanToolButton.cpp \ + widgets/LevelPanWidget.cpp \ widgets/ListInputDialog.cpp \ widgets/MIDIFileImportDialog.cpp \ widgets/ModelDataTableDialog.cpp \ @@ -163,5 +184,6 @@ widgets/Thumbwheel.cpp \ widgets/TipDialog.cpp \ widgets/TransformFinder.cpp \ + widgets/UnitConverter.cpp \ widgets/WindowShapePreview.cpp \ widgets/WindowTypeSelector.cpp diff -r 282f4be8f058 -r c02c51ae5238 view/LayerGeometryProvider.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/view/LayerGeometryProvider.h Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,174 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef LAYER_GEOMETRY_PROVIDER_H +#define LAYER_GEOMETRY_PROVIDER_H + +#include "base/BaseTypes.h" + +#include +#include +#include + +class ViewManager; +class View; +class Layer; + +class LayerGeometryProvider +{ +protected: + static int getNextId() { + static QMutex idMutex; + static int nextId = 1; + static int maxId = INT_MAX; + QMutexLocker locker(&idMutex); + int id = nextId; + if (nextId == maxId) { + // we don't expect this to happen in the lifetime of a + // process, but it would be undefined behaviour if it did + // since we're using a signed int, so we should really + // guard for it... + nextId = 1; + } else { + nextId++; + } + return id; + } + +public: + LayerGeometryProvider() { } + + /** + * Retrieve the id of this object. + */ + virtual int getId() const = 0; + + /** + * Retrieve the first visible sample frame on the widget. + * This is a calculated value based on the centre-frame, widget + * width and zoom level. The result may be negative. + */ + virtual sv_frame_t getStartFrame() const = 0; + + /** + * Return the centre frame of the visible widget. This is an + * exact value that does not depend on the zoom block size. Other + * frame values (start, end) are calculated from this based on the + * zoom and other factors. + */ + virtual sv_frame_t getCentreFrame() const = 0; + + /** + * Retrieve the last visible sample frame on the widget. + * This is a calculated value based on the centre-frame, widget + * width and zoom level. + */ + virtual sv_frame_t getEndFrame() const = 0; + + /** + * Return the pixel x-coordinate corresponding to a given sample + * frame (which may be negative). + */ + virtual int getXForFrame(sv_frame_t frame) const = 0; + + /** + * Return the closest frame to the given pixel x-coordinate. + */ + virtual sv_frame_t getFrameForX(int x) const = 0; + + virtual sv_frame_t getModelsStartFrame() const = 0; + virtual sv_frame_t getModelsEndFrame() const = 0; + + /** + * Return the closest pixel x-coordinate corresponding to a given + * view x-coordinate. + */ + virtual int getXForViewX(int viewx) const = 0; + + /** + * Return the closest view x-coordinate corresponding to a given + * pixel x-coordinate. + */ + virtual int getViewXForX(int x) const = 0; + + /** + * Return the pixel y-coordinate corresponding to a given + * frequency, if the frequency range is as specified. This does + * not imply any policy about layer frequency ranges, but it might + * be useful for layers to match theirs up if desired. + * + * Not thread-safe in logarithmic mode. Call only from GUI thread. + */ + virtual double getYForFrequency(double frequency, double minFreq, double maxFreq, + bool logarithmic) const = 0; + + /** + * Return the closest frequency to the given pixel y-coordinate, + * if the frequency range is as specified. + * + * Not thread-safe in logarithmic mode. Call only from GUI thread. + */ + virtual double getFrequencyForY(int y, double minFreq, double maxFreq, + bool logarithmic) const = 0; + + virtual int getTextLabelHeight(const Layer *layer, QPainter &) const = 0; + + virtual bool getValueExtents(QString unit, double &min, double &max, + bool &log) const = 0; + + /** + * Return the zoom level, i.e. the number of frames per pixel + */ + virtual int getZoomLevel() const = 0; + + /** + * To be called from a layer, to obtain the extent of the surface + * that the layer is currently painting to. This may be the extent + * of the view (if 1x display scaling is in effect) or of a larger + * cached pixmap (if greater display scaling is in effect). + */ + virtual QRect getPaintRect() const = 0; + + virtual QSize getPaintSize() const { return getPaintRect().size(); } + virtual int getPaintWidth() const { return getPaintRect().width(); } + virtual int getPaintHeight() const { return getPaintRect().height(); } + + virtual bool hasLightBackground() const = 0; + virtual QColor getForeground() const = 0; + virtual QColor getBackground() const = 0; + + virtual ViewManager *getViewManager() const = 0; + + virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const = 0; + virtual bool shouldShowFeatureLabels() const = 0; + + enum TextStyle { + BoxedText, + OutlinedText, + OutlinedItalicText + }; + + virtual void drawVisibleText(QPainter &p, int x, int y, + QString text, TextStyle style) const = 0; + + virtual void drawMeasurementRect(QPainter &p, const Layer *, + QRect rect, bool focus) const = 0; + + virtual void updatePaintRect(QRect r) = 0; + + virtual View *getView() = 0; + virtual const View *getView() const = 0; +}; + +#endif diff -r 282f4be8f058 -r c02c51ae5238 view/Overview.cpp --- a/view/Overview.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/view/Overview.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -25,26 +25,29 @@ //#define DEBUG_OVERVIEW 1 - - Overview::Overview(QWidget *w) : View(w, false), - m_clickedInRange(false) + m_clickedInRange(false), + m_dragCentreFrame(0) { setObjectName(tr("Overview")); m_followPan = false; m_followZoom = false; setPlaybackFollow(PlaybackIgnore); m_modelTestTime.start(); + + bool light = hasLightBackground(); + if (light) m_boxColour = Qt::darkGray; + else m_boxColour = Qt::lightGray; } void -Overview::modelChanged(size_t startFrame, size_t endFrame) +Overview::modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame) { bool zoomChanged = false; - size_t frameCount = getModelsEndFrame() - getModelsStartFrame(); - int zoomLevel = frameCount / width(); + sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame(); + int zoomLevel = int(frameCount / width()); if (zoomLevel < 1) zoomLevel = 1; zoomLevel = getZoomConstraintBlockSize(zoomLevel, ZoomConstraint::RoundUp); @@ -54,8 +57,8 @@ if (!zoomChanged) { if (m_modelTestTime.elapsed() < 1000) { - for (LayerList::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { if ((*i)->getModel() && (!(*i)->getModel()->isOK() || !(*i)->getModel()->isReady())) { @@ -67,7 +70,7 @@ } } - View::modelChanged(startFrame, endFrame); + View::modelChangedWithin(startFrame, endFrame); } void @@ -92,7 +95,11 @@ } void -Overview::globalCentreFrameChanged(unsigned long f) +Overview::globalCentreFrameChanged(sv_frame_t +#ifdef DEBUG_OVERVIEW + f +#endif + ) { #ifdef DEBUG_OVERVIEW cerr << "Overview::globalCentreFrameChanged: " << f << endl; @@ -101,7 +108,11 @@ } void -Overview::viewCentreFrameChanged(View *v, unsigned long f) +Overview::viewCentreFrameChanged(View *v, sv_frame_t +#ifdef DEBUG_OVERVIEW + f +#endif + ) { #ifdef DEBUG_OVERVIEW cerr << "Overview[" << this << "]::viewCentreFrameChanged(" << v << "): " << f << endl; @@ -112,7 +123,7 @@ } void -Overview::viewZoomLevelChanged(View *v, unsigned long, bool) +Overview::viewZoomLevelChanged(View *v, int, bool) { if (v == this) return; if (m_views.find(v) != m_views.end()) { @@ -121,7 +132,7 @@ } void -Overview::viewManagerPlaybackFrameChanged(unsigned long f) +Overview::viewManagerPlaybackFrameChanged(sv_frame_t f) { #ifdef DEBUG_OVERVIEW cerr << "Overview[" << this << "]::viewManagerPlaybackFrameChanged(" << f << "): " << f << endl; @@ -137,6 +148,26 @@ if (changed) update(); } +QColor +Overview::getFillWithin() const +{ + return Qt::transparent; +} + +QColor +Overview::getFillWithout() const +{ + QColor c = palette().window().color(); + c.setAlpha(100); + return c; +} + +void +Overview::setBoxColour(QColor c) +{ + m_boxColour = c; +} + void Overview::paintEvent(QPaintEvent *e) { @@ -146,9 +177,9 @@ cerr << "Overview::paintEvent: width is " << width() << ", centre frame " << m_centreFrame << endl; #endif - size_t startFrame = getModelsStartFrame(); - size_t frameCount = getModelsEndFrame() - getModelsStartFrame(); - int zoomLevel = frameCount / width(); + sv_frame_t startFrame = getModelsStartFrame(); + sv_frame_t frameCount = getModelsEndFrame() - getModelsStartFrame(); + int zoomLevel = int(frameCount / width()); if (zoomLevel < 1) zoomLevel = 1; zoomLevel = getZoomConstraintBlockSize(zoomLevel, ZoomConstraint::RoundUp); @@ -157,7 +188,7 @@ emit zoomLevelChanged(m_zoomLevel, m_followZoom); } - size_t centreFrame = startFrame + m_zoomLevel * (width() / 2); + sv_frame_t centreFrame = startFrame + m_zoomLevel * (width() / 2); if (centreFrame > (startFrame + getModelsEndFrame())/2) { centreFrame = (startFrame + getModelsEndFrame())/2; } @@ -177,50 +208,73 @@ QPainter paint; paint.begin(this); - + paint.setClipRegion(e->region()); + paint.setRenderHints(QPainter::Antialiasing); + QRect r(rect()); - if (e) { - r = e->rect(); - paint.setClipRect(r); - } + // We paint a rounded rect for each distinct set of view extents, + // and we colour in the inside and outside of the rect that + // corresponds to the current view. (One small caveat -- we don't + // know which rect that is yet. We'll have to figure it out + // somehow...) - paint.setPen(getForeground()); + std::set > extents; + std::vector rects; + QRect primary; int y = 0; - int prevx0 = -10; - int prevx1 = -10; - for (ViewSet::iterator i = m_views.begin(); i != m_views.end(); ++i) { if (!*i) continue; View *w = (View *)*i; - long f0 = w->getFrameForX(0); - long f1 = w->getFrameForX(w->width()); + sv_frame_t f0 = w->getFrameForX(0); + sv_frame_t f1 = w->getFrameForX(w->width()); if (f0 >= 0) { - size_t rf0 = w->alignToReference(f0); + sv_frame_t rf0 = w->alignToReference(f0); f0 = alignFromReference(rf0); } if (f1 >= 0) { - size_t rf1 = w->alignToReference(f1); + sv_frame_t rf1 = w->alignToReference(f1); f1 = alignFromReference(rf1); } int x0 = getXForFrame(f0); int x1 = getXForFrame(f1); - if (x0 != prevx0 || x1 != prevx1) { - y += height() / 10 + 1; - prevx0 = x0; - prevx1 = x1; - } if (x1 <= x0) x1 = x0 + 1; - - paint.drawRect(x0, y, x1 - x0, height() - 2 * y); + + std::pair extent(x0, x1); + + if (extents.find(extent) == extents.end()) { + + y += height() / 10 + 1; + extents.insert(extent); + + QRect vr(x0, y, x1 - x0, height() - 2 * y); + rects.push_back(vr); + primary = vr; //!!! for now + } + } + + QPainterPath without; + without.addRoundedRect(primary, 4, 4); + without.addRect(rect()); + paint.setPen(Qt::NoPen); + paint.setBrush(getFillWithout()); + paint.drawPath(without); + + paint.setBrush(getFillWithin()); + paint.drawRoundedRect(primary, 4, 4); + + foreach (QRect vr, rects) { + paint.setBrush(Qt::NoBrush); + paint.setPen(QPen(m_boxColour, 2)); + paint.drawRoundedRect(vr, 4, 4); } paint.end(); @@ -230,7 +284,7 @@ Overview::mousePressEvent(QMouseEvent *e) { m_clickPos = e->pos(); - long clickFrame = getFrameForX(m_clickPos.x()); + sv_frame_t clickFrame = getFrameForX(m_clickPos.x()); if (clickFrame > 0) m_dragCentreFrame = clickFrame; else m_dragCentreFrame = 0; m_clickedInRange = true; @@ -257,13 +311,13 @@ { if (!m_clickedInRange) return; - long xoff = int(e->x()) - int(m_clickPos.x()); - long frameOff = xoff * m_zoomLevel; + int xoff = int(e->x()) - int(m_clickPos.x()); + sv_frame_t frameOff = xoff * m_zoomLevel; - size_t newCentreFrame = m_dragCentreFrame; + sv_frame_t newCentreFrame = m_dragCentreFrame; if (frameOff > 0) { newCentreFrame += frameOff; - } else if (newCentreFrame >= size_t(-frameOff)) { + } else if (newCentreFrame >= -frameOff) { newCentreFrame += frameOff; } else { newCentreFrame = 0; @@ -275,20 +329,25 @@ } if (std::max(m_centreFrame, newCentreFrame) - - std::min(m_centreFrame, newCentreFrame) > size_t(m_zoomLevel)) { - size_t rf = alignToReference(newCentreFrame); + std::min(m_centreFrame, newCentreFrame) > m_zoomLevel) { + sv_frame_t rf = alignToReference(newCentreFrame); #ifdef DEBUG_OVERVIEW cerr << "Overview::mouseMoveEvent: x " << e->x() << " and click x " << m_clickPos.x() << " -> frame " << newCentreFrame << " -> rf " << rf << endl; #endif - emit centreFrameChanged(rf, true, PlaybackScrollContinuous); + if (m_followPlay == PlaybackScrollContinuous || + m_followPlay == PlaybackScrollPageWithCentre) { + emit centreFrameChanged(rf, true, PlaybackScrollContinuous); + } else { + emit centreFrameChanged(rf, true, PlaybackIgnore); + } } } void Overview::mouseDoubleClickEvent(QMouseEvent *e) { - long frame = getFrameForX(e->x()); - size_t rf = 0; + sv_frame_t frame = getFrameForX(e->x()); + sv_frame_t rf = 0; if (frame > 0) rf = alignToReference(frame); #ifdef DEBUG_OVERVIEW cerr << "Overview::mouseDoubleClickEvent: frame " << frame << " -> rf " << rf << endl; diff -r 282f4be8f058 -r c02c51ae5238 view/Overview.h --- a/view/Overview.h Tue Jul 14 15:04:46 2015 +0100 +++ b/view/Overview.h Wed Apr 20 12:06:28 2016 +0100 @@ -41,14 +41,16 @@ virtual QString getPropertyContainerIconName() const { return "panner"; } public slots: - virtual void modelChanged(size_t startFrame, size_t endFrame); + virtual void modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame); virtual void modelReplaced(); - virtual void globalCentreFrameChanged(unsigned long); - virtual void viewCentreFrameChanged(View *, unsigned long); - virtual void viewZoomLevelChanged(View *, unsigned long, bool); - virtual void viewManagerPlaybackFrameChanged(unsigned long); + virtual void globalCentreFrameChanged(sv_frame_t); + virtual void viewCentreFrameChanged(View *, sv_frame_t); + virtual void viewZoomLevelChanged(View *, int, bool); + virtual void viewManagerPlaybackFrameChanged(sv_frame_t); + virtual void setBoxColour(QColor); + protected: virtual void paintEvent(QPaintEvent *e); virtual void mousePressEvent(QMouseEvent *e); @@ -59,11 +61,15 @@ virtual void leaveEvent(QEvent *); virtual bool shouldLabelSelections() const { return false; } + QColor getFillWithin() const; + QColor getFillWithout() const; + QPoint m_clickPos; QPoint m_mousePos; bool m_clickedInRange; - size_t m_dragCentreFrame; + sv_frame_t m_dragCentreFrame; QTime m_modelTestTime; + QColor m_boxColour; typedef std::set ViewSet; ViewSet m_views; diff -r 282f4be8f058 -r c02c51ae5238 view/Pane.cpp --- a/view/Pane.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/view/Pane.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -24,6 +24,12 @@ #include "widgets/TextAbbrev.h" #include "base/Preferences.h" #include "layer/WaveformLayer.h" +#include "layer/TimeRulerLayer.h" + +// GF: added so we can propagate the mouse move event to the note layer for context handling. +#include "layer/LayerFactory.h" +#include "layer/FlexiNoteLayer.h" + //!!! ugh #include "data/model/WaveFileModel.h" @@ -36,6 +42,7 @@ #include #include #include +#include #include #include @@ -72,12 +79,15 @@ m_releasing(false), m_centreLineVisible(true), m_scaleWidth(0), + m_pendingWheelAngle(0), m_headsUpDisplay(0), m_vpan(0), m_hthumb(0), m_vthumb(0), m_reset(0), - m_mouseInWidget(false) + m_mouseInWidget(false), + m_playbackFrameMoveScheduled(false), + m_playbackFrameMoveTo(0) { setObjectName("Pane"); setMouseTracking(true); @@ -85,8 +95,10 @@ updateHeadsUpDisplay(); - -// SVDEBUG << "Pane::Pane(" << this << ") returning" << endl; + connect(this, SIGNAL(regionOutlined(QRect)), + this, SLOT(zoomToRegion(QRect))); + + cerr << "Pane::Pane(" << this << ") returning" << endl; } void @@ -132,7 +144,7 @@ m_hthumb->setFixedWidth(70); m_hthumb->setFixedHeight(16); m_hthumb->setDefaultValue(0); - m_hthumb->setSpeed(0.6); + m_hthumb->setSpeed(0.6f); connect(m_hthumb, SIGNAL(valueChanged(int)), this, SLOT(horizontalThumbwheelMoved(int))); connect(m_hthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget())); @@ -168,7 +180,7 @@ } m_reset = new NotifyingPushButton; - m_reset->setFlat(true); + m_reset->setFlat(true); m_reset->setCursor(Qt::ArrowCursor); m_reset->setFixedHeight(16); m_reset->setFixedWidth(16); @@ -191,7 +203,7 @@ //!!! pull out into function (presumably in View) bool haveConstraint = false; - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) { haveConstraint = true; @@ -309,12 +321,12 @@ return; } - float vmin, vmax, dmin, dmax; + double vmin, vmax, dmin, dmax; if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax) && vmax != vmin) { - float y0 = (dmin - vmin) / (vmax - vmin); - float y1 = (dmax - vmin) / (vmax - vmin); + double y0 = (dmin - vmin) / (vmax - vmin); + double y1 = (dmax - vmin) / (vmax - vmin); m_vpan->blockSignals(true); - m_vpan->setRectExtents(0, 1.0 - y1, 1, y1 - y0); + m_vpan->setRectExtents(0, float(1.0 - y1), 1, float(y1 - y0)); m_vpan->blockSignals(false); m_vpan->show(); } else { @@ -328,19 +340,19 @@ QPoint discard; bool b0, b1; - if (m_manager && m_manager->getToolMode() == ViewManager::MeasureMode) { + if (m_manager && m_manager->getToolModeFor(this) == ViewManager::MeasureMode) { return false; } - + if (m_manager && !m_manager->shouldIlluminateLocalFeatures()) { return false; } - if (layer == getSelectedLayer() && - !shouldIlluminateLocalSelection(discard, b0, b1)) { - - pos = m_identifyPoint; - return m_identifyFeatures; + if (layer == getInteractionLayer() && + !shouldIlluminateLocalSelection(discard, b0, b1)) { + + pos = m_identifyPoint; + return m_identifyFeatures; } return false; @@ -348,25 +360,25 @@ bool Pane::shouldIlluminateLocalSelection(QPoint &pos, - bool &closeToLeft, - bool &closeToRight) const + bool &closeToLeft, + bool &closeToRight) const { if (m_identifyFeatures && - m_manager && - m_manager->getToolMode() == ViewManager::EditMode && - !m_manager->getSelections().empty() && - !selectionIsBeingEdited()) { - - Selection s(getSelectionAt(m_identifyPoint.x(), - closeToLeft, closeToRight)); - - if (!s.isEmpty()) { - if (getSelectedLayer() && getSelectedLayer()->isLayerEditable()) { - - pos = m_identifyPoint; - return true; - } - } + m_manager && + m_manager->getToolModeFor(this) == ViewManager::EditMode && + !m_manager->getSelections().empty() && + !selectionIsBeingEdited()) { + + Selection s(getSelectionAt(m_identifyPoint.x(), + closeToLeft, closeToRight)); + + if (!s.isEmpty()) { + if (getInteractionLayer() && getInteractionLayer()->isLayerEditable()) { + + pos = m_identifyPoint; + return true; + } + } } return false; @@ -376,10 +388,10 @@ Pane::selectionIsBeingEdited() const { if (!m_editingSelection.isEmpty()) { - if (m_mousePos != m_clickPos && - getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) { - return true; - } + if (m_mousePos != m_clickPos && + getFrameForX(m_mousePos.x()) != getFrameForX(m_clickPos.x())) { + return true; + } } return false; } @@ -408,14 +420,14 @@ if (e) paint.setClipRect(r); - ViewManager::ToolMode toolMode = m_manager->getToolMode(); + ViewManager::ToolMode toolMode = ViewManager::NavigateMode; + if (m_manager) toolMode = m_manager->getToolModeFor(this); if (m_manager && -// !m_manager->isPlaying() && m_mouseInWidget && toolMode == ViewManager::MeasureMode) { - for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { + for (LayerList::iterator vi = m_layerStack.end(); vi != m_layerStack.begin(); ) { --vi; std::vector crosshairExtents; @@ -436,7 +448,7 @@ const Model *waveformModel = 0; // just for reporting purposes const Model *workModel = 0; - for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { + for (LayerList::iterator vi = m_layerStack.end(); vi != m_layerStack.begin(); ) { --vi; if (!haveSomeTimeXAxis && (*vi)->hasTimeXAxis()) { haveSomeTimeXAxis = true; @@ -458,6 +470,10 @@ m_scaleWidth = 0; + if (workModel && hasTopLayerTimeXAxis()) { + drawModelTimeExtents(r, paint, workModel); + } + if (m_manager && m_manager->shouldShowVerticalScale() && topLayer) { drawVerticalScale(r, topLayer, paint); } @@ -468,7 +484,7 @@ drawFeatureDescription(topLayer, paint); } - int sampleRate = getModelsSampleRate(); + sv_samplerate_t sampleRate = getModelsSampleRate(); paint.setBrush(Qt::NoBrush); if (m_centreLineVisible && @@ -480,6 +496,7 @@ paint.setPen(QColor(50, 50, 50)); if (waveformModel && + sampleRate && m_manager && m_manager->shouldShowDuration()) { drawDurationAndRate(r, waveformModel, sampleRate, paint); @@ -532,7 +549,7 @@ paint.end(); } -size_t +int Pane::getVerticalScaleWidth() const { if (m_scaleWidth > 0) return m_scaleWidth; @@ -544,7 +561,7 @@ { Layer *scaleLayer = 0; - float min, max; + double min, max; bool log; QString unit; @@ -571,8 +588,8 @@ if (!hasValueExtents) { - for (LayerList::iterator vi = m_layers.end(); - vi != m_layers.begin(); ) { + for (LayerList::iterator vi = m_layerStack.end(); + vi != m_layerStack.begin(); ) { --vi; @@ -591,8 +608,8 @@ QString requireUnit = unit; - for (LayerList::iterator vi = m_layers.end(); - vi != m_layers.begin(); ) { + for (LayerList::iterator vi = m_layerStack.end(); + vi != m_layerStack.begin(); ) { --vi; @@ -626,9 +643,9 @@ if (m_scaleWidth > 0 && r.left() < m_scaleWidth) { -// Profiler profiler("Pane::paintEvent - painting vertical scale", true); - -// SVDEBUG << "Pane::paintEvent: calling paint.save() in vertical scale block" << endl; +// Profiler profiler("Pane::paintEvent - painting vertical scale", true); + +// SVDEBUG << "Pane::paintEvent: calling paint.save() in vertical scale block" << endl; paint.save(); paint.setPen(getForeground()); @@ -649,7 +666,7 @@ { QPoint pos = m_identifyPoint; QString desc = topLayer->getFeatureDescription(this, pos); - + if (desc != "") { paint.save(); @@ -698,7 +715,7 @@ } void -Pane::drawCentreLine(int sampleRate, QPainter &paint, bool omitLine) +Pane::drawCentreLine(sv_samplerate_t sampleRate, QPainter &paint, bool omitLine) { int fontHeight = paint.fontMetrics().height(); int fontAscent = paint.fontMetrics().ascent(); @@ -723,10 +740,10 @@ int y = height() - fontHeight + fontAscent - 6; - LayerList::iterator vi = m_layers.end(); + LayerList::iterator vi = m_layerStack.end(); - if (vi != m_layers.begin()) { - + if (vi != m_layerStack.begin()) { + switch ((*--vi)->getPreferredFrameCountPosition()) { case Layer::PositionTop: @@ -768,6 +785,37 @@ } void +Pane::drawModelTimeExtents(QRect r, QPainter &paint, const Model *model) +{ + int x0 = getXForFrame(model->getStartFrame()); + int x1 = getXForFrame(model->getEndFrame()); + + paint.save(); + + QBrush brush; + + if (hasLightBackground()) { + brush = QBrush(QColor("#f8f8f8")); + paint.setPen(Qt::black); + } else { + brush = QBrush(QColor("#101010")); + paint.setPen(Qt::white); + } + + if (x0 > r.x()) { + paint.fillRect(0, 0, x0, height(), brush); + paint.drawLine(x0, 0, x0, height()); + } + + if (x1 < r.x() + r.width()) { + paint.fillRect(x1, 0, width() - x1, height(), brush); + paint.drawLine(x1, 0, x1, height()); + } + + paint.restore(); +} + +void Pane::drawAlignmentStatus(QRect r, QPainter &paint, const Model *model, bool down) { @@ -870,13 +918,13 @@ lly -= 20; } - if (r.y() + r.height() < lly - int(m_layers.size()) * fontHeight) { + if (r.y() + r.height() < lly - int(m_layerStack.size()) * fontHeight) { return; } QStringList texts; std::vector pixmaps; - for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { texts.push_back((*i)->getLayerPresentationName()); // cerr << "Pane " << this << ": Layer presentation name for " << *i << ": " // << texts[texts.size()-1] << endl; @@ -893,8 +941,8 @@ } if (r.x() + r.width() >= llx - fontAscent - 3) { - - for (size_t i = 0; i < texts.size(); ++i) { + + for (int i = 0; i < texts.size(); ++i) { // cerr << "Pane "<< this << ": text " << i << ": " << texts[i] << endl; @@ -922,7 +970,7 @@ { int offset = m_mousePos.x() - m_clickPos.x(); - long origStart = m_editingSelection.getStartFrame(); + sv_frame_t origStart = m_editingSelection.getStartFrame(); int p0 = getXForFrame(origStart) + offset; int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset; @@ -933,15 +981,15 @@ p0 = getXForFrame(m_editingSelection.getStartFrame()); } - long newStart = getFrameForX(p0); - long newEnd = getFrameForX(p1); + sv_frame_t newStart = getFrameForX(p0); + sv_frame_t newEnd = getFrameForX(p1); paint.save(); paint.setPen(QPen(getForeground(), 2)); int fontHeight = paint.fontMetrics().height(); int fontAscent = paint.fontMetrics().ascent(); - int sampleRate = getModelsSampleRate(); + sv_samplerate_t sampleRate = getModelsSampleRate(); QString startText, endText, offsetText; startText = QString("%1").arg(newStart); endText = QString("%1").arg(newEnd); @@ -990,17 +1038,17 @@ void Pane::drawDurationAndRate(QRect r, const Model *waveformModel, - int sampleRate, QPainter &paint) + sv_samplerate_t sampleRate, QPainter &paint) { int fontHeight = paint.fontMetrics().height(); int fontAscent = paint.fontMetrics().ascent(); if (r.y() + r.height() < height() - fontHeight - 6) return; - size_t modelRate = waveformModel->getSampleRate(); - size_t nativeRate = waveformModel->getNativeRate(); - size_t playbackRate = m_manager->getPlaybackSampleRate(); - size_t outputRate = m_manager->getOutputSampleRate(); + sv_samplerate_t modelRate = waveformModel->getSampleRate(); + sv_samplerate_t nativeRate = waveformModel->getNativeRate(); + sv_samplerate_t playbackRate = m_manager->getPlaybackSampleRate(); + sv_samplerate_t outputRate = m_manager->getOutputSampleRate(); QString srNote = ""; @@ -1039,7 +1087,7 @@ } bool -Pane::render(QPainter &paint, int xorigin, size_t f0, size_t f1) +Pane::render(QPainter &paint, int xorigin, sv_frame_t f0, sv_frame_t f1) { if (!View::render(paint, xorigin + m_scaleWidth, f0, f1)) { return false; @@ -1047,8 +1095,9 @@ if (m_scaleWidth > 0) { - for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { - --vi; + Layer *layer = getTopLayer(); + + if (layer) { paint.save(); @@ -1057,12 +1106,11 @@ paint.drawRect(xorigin, -1, m_scaleWidth, height()+1); paint.setBrush(Qt::NoBrush); - (*vi)->paintVerticalScale + layer->paintVerticalScale (this, m_manager->shouldShowVerticalColourScale(), paint, QRect(xorigin, 0, m_scaleWidth, height())); paint.restore(); - break; } } @@ -1070,10 +1118,10 @@ } QImage * -Pane::toNewImage(size_t f0, size_t f1) +Pane::toNewImage(sv_frame_t f0, sv_frame_t f1) { - size_t x0 = f0 / getZoomLevel(); - size_t x1 = f1 / getZoomLevel(); + int x0 = int(f0 / getZoomLevel()); + int x1 = int(f1 / getZoomLevel()); QImage *image = new QImage(x1 - x0 + m_scaleWidth, height(), QImage::Format_RGB32); @@ -1081,12 +1129,11 @@ int formerScaleWidth = m_scaleWidth; if (m_manager && m_manager->shouldShowVerticalScale()) { - for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { - --vi; + Layer *layer = getTopLayer(); + if (layer) { QPainter paint(image); - m_scaleWidth = (*vi)->getVerticalScaleWidth + m_scaleWidth = layer->getVerticalScaleWidth (this, m_manager->shouldShowVerticalColourScale(), paint); - break; } } else { m_scaleWidth = 0; @@ -1110,7 +1157,7 @@ } QSize -Pane::getImageSize(size_t f0, size_t f1) +Pane::getImageSize(sv_frame_t f0, sv_frame_t f1) { QSize s = View::getImageSize(f0, f1); QImage *image = new QImage(100, 100, QImage::Format_RGB32); @@ -1118,23 +1165,22 @@ int sw = 0; if (m_manager && m_manager->shouldShowVerticalScale()) { - for (LayerList::iterator vi = m_layers.end(); vi != m_layers.begin(); ) { - --vi; - sw = (*vi)->getVerticalScaleWidth + Layer *layer = getTopLayer(); + if (layer) { + sw = layer->getVerticalScaleWidth (this, m_manager->shouldShowVerticalColourScale(), paint); - break; } } return QSize(sw + s.width(), s.height()); } -size_t +sv_frame_t Pane::getFirstVisibleFrame() const { - long f0 = getFrameForX(m_scaleWidth); - size_t f = View::getFirstVisibleFrame(); - if (f0 < 0 || f0 < long(f)) return f; + sv_frame_t f0 = getFrameForX(m_scaleWidth); + sv_frame_t f = View::getFirstVisibleFrame(); + if (f0 < 0 || f0 < f) return f; return f0; } @@ -1145,10 +1191,10 @@ if (!m_manager) return Selection(); - long testFrame = getFrameForX(x - 5); + sv_frame_t testFrame = getFrameForX(x - 5); if (testFrame < 0) { - testFrame = getFrameForX(x); - if (testFrame < 0) return Selection(); + testFrame = getFrameForX(x); + if (testFrame < 0) return Selection(); } Selection selection = m_manager->getContainingSelection(testFrame, true); @@ -1174,15 +1220,15 @@ bool Pane::canTopLayerMoveVertical() { - float vmin, vmax, dmin, dmax; + double vmin, vmax, dmin, dmax; if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return false; if (dmin <= vmin && dmax >= vmax) return false; return true; } bool -Pane::getTopLayerDisplayExtents(float &vmin, float &vmax, - float &dmin, float &dmax, +Pane::getTopLayerDisplayExtents(double &vmin, double &vmax, + double &dmin, double &dmax, QString *unit) { Layer *layer = getTopLayer(); @@ -1196,7 +1242,7 @@ } bool -Pane::setTopLayerDisplayExtents(float dmin, float dmax) +Pane::setTopLayerDisplayExtents(double dmin, double dmax) { Layer *layer = getTopLayer(); if (!layer) return false; @@ -1226,47 +1272,18 @@ tr("Double-click middle button to relocate with any tool")); kr.registerShortcut(tr("Menu"), tr("Right"), tr("Show pane context menu")); - - kr.setCategory(tr("Navigate Tool Mouse Actions")); - - kr.registerShortcut(tr("Navigate"), tr("Left"), - tr("Click left button and drag to move around")); - kr.registerShortcut(tr("Zoom to Area"), tr("Shift+Left"), - tr("Shift-click left button and drag to zoom to a rectangular area")); - kr.registerShortcut(tr("Relocate"), tr("Double-Click Left"), - tr("Double-click left button to jump to clicked location")); - kr.registerShortcut(tr("Edit"), tr("Double-Click Left"), - tr("Double-click left button on an item to edit it")); - - kr.setCategory(tr("Select Tool Mouse Actions")); - kr.registerShortcut(tr("Select"), tr("Left"), - tr("Click left button and drag to select region; drag region edge to resize")); - kr.registerShortcut(tr("Multi Select"), tr("Ctrl+Left"), -#ifdef Q_OS_MAC - tr("Cmd-click left button and drag to select an additional region")); -#else - tr("Ctrl-click left button and drag to select an additional region")); -#endif - kr.registerShortcut(tr("Fine Select"), tr("Shift+Left"), - tr("Shift-click left button and drag to select without snapping to items or grid")); - - kr.setCategory(tr("Edit Tool Mouse Actions")); - kr.registerShortcut(tr("Move"), tr("Left"), - tr("Click left button on an item or selected region and drag to move")); - kr.registerShortcut(tr("Edit"), tr("Double-Click Left"), - tr("Double-click left button on an item to edit it")); - - kr.setCategory(tr("Draw Tool Mouse Actions")); - kr.registerShortcut(tr("Draw"), tr("Left"), - tr("Click left button and drag to create new item")); - - kr.setCategory(tr("Measure Tool Mouse Actions")); - kr.registerShortcut(tr("Measure Area"), tr("Left"), - tr("Click left button and drag to measure a rectangular area")); - kr.registerShortcut(tr("Measure Item"), tr("Double-Click Left"), - tr("Click left button and drag to measure extents of an item or shape")); - kr.registerShortcut(tr("Zoom to Area"), tr("Shift+Left"), - tr("Shift-click left button and drag to zoom to a rectangular area")); +} + +Layer * +Pane::getTopFlexiNoteLayer() +{ + for (int i = int(m_layerStack.size()) - 1; i >= 0; --i) { + if (LayerFactory::getInstance()->getLayerType(m_layerStack[i]) == + LayerFactory::FlexiNotes) { + return m_layerStack[i]; + } + } + return 0; } void @@ -1291,7 +1308,7 @@ m_dragMode = UnresolvedDrag; ViewManager::ToolMode mode = ViewManager::NavigateMode; - if (m_manager) mode = m_manager->getToolMode(); + if (m_manager) mode = m_manager->getToolModeFor(this); m_navigating = false; m_resizing = false; @@ -1303,78 +1320,104 @@ (mode == ViewManager::MeasureMode && (e->buttons() & Qt::LeftButton) && m_shiftPressed)) { - if (mode != ViewManager::NavigateMode) { - setCursor(Qt::PointingHandCursor); - } - - m_navigating = true; - m_dragCentreFrame = m_centreFrame; + if (mode != ViewManager::NavigateMode) { + setCursor(Qt::PointingHandCursor); + } + + m_navigating = true; + m_dragCentreFrame = m_centreFrame; m_dragStartMinValue = 0; - float vmin, vmax, dmin, dmax; + double vmin, vmax, dmin, dmax; if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) { m_dragStartMinValue = dmin; } + if (m_followPlay == PlaybackScrollPage) { + // Schedule a play-head move to the mouse frame + // location. This will happen only if nothing else of + // interest happens (double-click, drag) before the + // timeout. + schedulePlaybackFrameMove(getFrameForX(e->x())); + } + } else if (mode == ViewManager::SelectMode) { if (!hasTopLayerTimeXAxis()) return; - bool closeToLeft = false, closeToRight = false; - Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight); - - if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { - - m_manager->removeSelection(selection); - - if (closeToLeft) { - m_selectionStartFrame = selection.getEndFrame(); - } else { - m_selectionStartFrame = selection.getStartFrame(); - } - - m_manager->setInProgressSelection(selection, false); - m_resizing = true; - - } else { - - int mouseFrame = getFrameForX(e->x()); - size_t resolution = 1; - int snapFrame = mouseFrame; - - Layer *layer = getSelectedLayer(); - if (layer && !m_shiftPressed) { - layer->snapToFeatureFrame(this, snapFrame, - resolution, Layer::SnapLeft); - } - - if (snapFrame < 0) snapFrame = 0; - m_selectionStartFrame = snapFrame; - if (m_manager) { - m_manager->setInProgressSelection + bool closeToLeft = false, closeToRight = false; + Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight); + + if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { + + m_manager->removeSelection(selection); + + if (closeToLeft) { + m_selectionStartFrame = selection.getEndFrame(); + } else { + m_selectionStartFrame = selection.getStartFrame(); + } + + m_manager->setInProgressSelection(selection, false); + m_resizing = true; + + } else { + + sv_frame_t mouseFrame = getFrameForX(e->x()); + int resolution = 1; + sv_frame_t snapFrame = mouseFrame; + + Layer *layer = getInteractionLayer(); + if (layer && !m_shiftPressed && + !qobject_cast(layer)) { // don't snap to secs + layer->snapToFeatureFrame(this, snapFrame, + resolution, Layer::SnapLeft); + } + + if (snapFrame < 0) snapFrame = 0; + m_selectionStartFrame = snapFrame; + if (m_manager) { + m_manager->setInProgressSelection (Selection(alignToReference(snapFrame), alignToReference(snapFrame + resolution)), !m_ctrlPressed); - } - - m_resizing = false; - } - - update(); + } + + m_resizing = false; + + if (m_followPlay == PlaybackScrollPage) { + // Schedule a play-head move to the mouse frame + // location. This will happen only if nothing else of + // interest happens (double-click, drag) before the + // timeout. + schedulePlaybackFrameMove(mouseFrame); + } + } + + update(); } else if (mode == ViewManager::DrawMode) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->drawStart(this, e); - } + Layer *layer = getInteractionLayer(); + if (layer && layer->isLayerEditable()) { + layer->drawStart(this, e); + } } else if (mode == ViewManager::EraseMode) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->eraseStart(this, e); - } + Layer *layer = getInteractionLayer(); + if (layer && layer->isLayerEditable()) { + layer->eraseStart(this, e); + } + + // GF: handle mouse press for NoteEditMode + } else if (mode == ViewManager::NoteEditMode) { + + std::cerr << "mouse pressed in note edit mode" << std::endl; + Layer *layer = getTopFlexiNoteLayer(); + if (layer) { + layer->splitStart(this, e); + } } else if (mode == ViewManager::EditMode) { @@ -1392,43 +1435,63 @@ } void +Pane::schedulePlaybackFrameMove(sv_frame_t frame) +{ + m_playbackFrameMoveTo = frame; + m_playbackFrameMoveScheduled = true; + QTimer::singleShot(QApplication::doubleClickInterval() + 10, this, + SLOT(playbackScheduleTimerElapsed())); +} + +void +Pane::playbackScheduleTimerElapsed() +{ + if (m_playbackFrameMoveScheduled) { + m_manager->setPlaybackFrame(m_playbackFrameMoveTo); + m_playbackFrameMoveScheduled = false; + } +} + +void Pane::mouseReleaseEvent(QMouseEvent *e) { - if (e->buttons() & Qt::RightButton) { + if (e && (e->buttons() & Qt::RightButton)) { return; } // cerr << "mouseReleaseEvent" << endl; ViewManager::ToolMode mode = ViewManager::NavigateMode; - if (m_manager) mode = m_manager->getToolMode(); + if (m_manager) mode = m_manager->getToolModeFor(this); m_releasing = true; if (m_clickedInRange) { - mouseMoveEvent(e); + mouseMoveEvent(e); } + sv_frame_t mouseFrame = e ? getFrameForX(e->x()) : 0; + if (mouseFrame < 0) mouseFrame = 0; + if (m_navigating || mode == ViewManager::NavigateMode) { - m_navigating = false; - - if (mode != ViewManager::NavigateMode) { - // restore cursor - toolModeChanged(); - } - - if (m_shiftPressed) { - - int x0 = std::min(m_clickPos.x(), m_mousePos.x()); - int x1 = std::max(m_clickPos.x(), m_mousePos.x()); - - int y0 = std::min(m_clickPos.y(), m_mousePos.y()); - int y1 = std::max(m_clickPos.y(), m_mousePos.y()); - - zoomToRegion(x0, y0, x1, y1); - - } + m_navigating = false; + + if (mode != ViewManager::NavigateMode) { + // restore cursor + toolModeChanged(); + } + + if (m_shiftPressed) { + + int x0 = std::min(m_clickPos.x(), m_mousePos.x()); + int x1 = std::max(m_clickPos.x(), m_mousePos.x()); + + int y0 = std::min(m_clickPos.y(), m_mousePos.y()); + int y1 = std::max(m_clickPos.y(), m_mousePos.y()); + + emit regionOutlined(QRect(x0, y0, x1 - x0, y1 - y0)); + } } else if (mode == ViewManager::SelectMode) { @@ -1437,53 +1500,71 @@ return; } - if (m_manager && m_manager->haveInProgressSelection()) { - - bool exclusive; - Selection selection = m_manager->getInProgressSelection(exclusive); - - if (selection.getEndFrame() < selection.getStartFrame() + 2) { - selection = Selection(); - } - - m_manager->clearInProgressSelection(); - - if (exclusive) { - m_manager->setSelection(selection); - } else { - m_manager->addSelection(selection); - } - } - - update(); + if (m_manager && m_manager->haveInProgressSelection()) { + + //cerr << "JTEST: release with selection" << endl; + bool exclusive; + Selection selection = m_manager->getInProgressSelection(exclusive); + + if (selection.getEndFrame() < selection.getStartFrame() + 2) { + selection = Selection(); + } + + m_manager->clearInProgressSelection(); + + if (exclusive) { + m_manager->setSelection(selection); + } else { + m_manager->addSelection(selection); + } + } + + update(); } else if (mode == ViewManager::DrawMode) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->drawEnd(this, e); - update(); - } + Layer *layer = getInteractionLayer(); + if (layer && layer->isLayerEditable()) { + layer->drawEnd(this, e); + update(); + } } else if (mode == ViewManager::EraseMode) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->eraseEnd(this, e); - update(); - } + Layer *layer = getInteractionLayer(); + if (layer && layer->isLayerEditable()) { + layer->eraseEnd(this, e); + update(); + } + + } else if (mode == ViewManager::NoteEditMode) { + + //GF: handle mouse release for NoteEditMode (note: works but will need to re-think this a bit later) + Layer *layer = getTopFlexiNoteLayer(); + + if (layer) { + layer->splitEnd(this, e); + update(); + + if (m_editing) { + if (!editSelectionEnd(e)) { + layer->editEnd(this, e); + update(); + } + } + } } else if (mode == ViewManager::EditMode) { - + if (m_editing) { if (!editSelectionEnd(e)) { - Layer *layer = getSelectedLayer(); + Layer *layer = getInteractionLayer(); if (layer && layer->isLayerEditable()) { layer->editEnd(this, e); update(); } } - } + } } else if (mode == ViewManager::MeasureMode) { @@ -1502,7 +1583,7 @@ void Pane::mouseMoveEvent(QMouseEvent *e) { - if (e->buttons() & Qt::RightButton) { + if (!e || (e->buttons() & Qt::RightButton)) { return; } @@ -1526,28 +1607,39 @@ } ViewManager::ToolMode mode = ViewManager::NavigateMode; - if (m_manager) mode = m_manager->getToolMode(); + if (m_manager) mode = m_manager->getToolModeFor(this); QPoint prevPoint = m_identifyPoint; m_identifyPoint = e->pos(); if (!m_clickedInRange) { - - if (mode == ViewManager::SelectMode && hasTopLayerTimeXAxis()) { - bool closeToLeft = false, closeToRight = false; - getSelectionAt(e->x(), closeToLeft, closeToRight); - if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { - setCursor(Qt::SizeHorCursor); - } else { - setCursor(Qt::ArrowCursor); - } - } - - if (!m_manager->isPlaying()) { + + // GF: handle mouse move for context sensitive cursor switching in NoteEditMode. + // GF: Propagate the event to FlexiNoteLayer. I somehow feel it's best handeled there rather than here, but perhaps not if this will be needed elsewhere too. + if (mode == ViewManager::NoteEditMode) { + FlexiNoteLayer *layer = qobject_cast(getTopFlexiNoteLayer()); + if (layer) { + layer->mouseMoveEvent(this, e); //!!! ew + update(); + // return; + } + } + + if (mode == ViewManager::SelectMode && hasTopLayerTimeXAxis()) { + bool closeToLeft = false, closeToRight = false; + getSelectionAt(e->x(), closeToLeft, closeToRight); + if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { + setCursor(Qt::SizeHorCursor); + } else { + setCursor(Qt::ArrowCursor); + } + } + + if (m_manager && !m_manager->isPlaying()) { bool updating = false; - if (getSelectedLayer() && + if (getInteractionLayer() && m_manager->shouldIlluminateLocalFeatures()) { bool previouslyIdentifying = m_identifyFeatures; @@ -1560,8 +1652,7 @@ } } - if (!updating && mode == ViewManager::MeasureMode && - m_manager && !m_manager->isPlaying()) { + if (!updating && mode == ViewManager::MeasureMode) { Layer *layer = getTopLayer(); if (layer && layer->nearestMeasurementRectChanged @@ -1571,17 +1662,17 @@ } } - return; + return; } if (m_navigating || mode == ViewManager::NavigateMode) { - if (m_shiftPressed) { - - m_mousePos = e->pos(); - update(); - - } else { + if (m_shiftPressed) { + + m_mousePos = e->pos(); + update(); + + } else { dragTopLayer(e); } @@ -1594,27 +1685,25 @@ } else if (mode == ViewManager::DrawMode) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->drawDrag(this, e); - } + Layer *layer = getInteractionLayer(); + if (layer && layer->isLayerEditable()) { + layer->drawDrag(this, e); + } } else if (mode == ViewManager::EraseMode) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - layer->eraseDrag(this, e); - } - - } else if (mode == ViewManager::EditMode) { + Layer *layer = getInteractionLayer(); + if (layer && layer->isLayerEditable()) { + layer->eraseDrag(this, e); + } + + // GF: handling NoteEditMode dragging and boundary actions for mouseMoveEvent + } else if (mode == ViewManager::NoteEditMode) { bool resist = true; if ((e->modifiers() & Qt::ShiftModifier)) { m_shiftPressed = true; - // ... but don't set it false if shift has been - // released -- we want the state when we started - // dragging to be used most of the time } if (m_shiftPressed) resist = false; @@ -1641,7 +1730,74 @@ e->modifiers()); if (!editSelectionStart(&clickEvent)) { - Layer *layer = getSelectedLayer(); + Layer *layer = getTopFlexiNoteLayer(); + if (layer) { + std::cerr << "calling edit start" << std::endl; + layer->editStart(this, &clickEvent); + } + } + } + + } else { + + if (!editSelectionDrag(e)) { + + Layer *layer = getTopFlexiNoteLayer(); + + if (layer) { + + int x = e->x(); + int y = e->y(); + if (m_dragMode == VerticalDrag) x = m_clickPos.x(); + else if (m_dragMode == HorizontalDrag) y = m_clickPos.y(); + + QMouseEvent moveEvent(QEvent::MouseMove, + QPoint(x, y), + Qt::NoButton, + e->buttons(), + e->modifiers()); + std::cerr << "calling editDrag" << std::endl; + layer->editDrag(this, &moveEvent); + } + } + } + + } else if (mode == ViewManager::EditMode) { + + bool resist = true; + + if ((e->modifiers() & Qt::ShiftModifier)) { + m_shiftPressed = true; + // ... but don't set it false if shift has been + // released -- we want the state when we started + // dragging to be used most of the time + } + + if (m_shiftPressed) resist = false; + + m_dragMode = updateDragMode + (m_dragMode, + m_clickPos, + e->pos(), + true, // can move horiz + true, // can move vert + resist, // resist horiz + resist); // resist vert + + if (!m_editing) { + + if (m_dragMode != UnresolvedDrag) { + + m_editing = true; + + QMouseEvent clickEvent(QEvent::MouseButtonPress, + m_clickPos, + Qt::NoButton, + e->buttons(), + e->modifiers()); + + if (!editSelectionStart(&clickEvent)) { + Layer *layer = getInteractionLayer(); if (layer && layer->isLayerEditable()) { layer->editStart(this, &clickEvent); } @@ -1652,7 +1808,7 @@ if (!editSelectionDrag(e)) { - Layer *layer = getSelectedLayer(); + Layer *layer = getInteractionLayer(); if (layer && layer->isLayerEditable()) { @@ -1684,27 +1840,36 @@ update(); } + + if (m_dragMode != UnresolvedDrag) { + m_playbackFrameMoveScheduled = false; + } } void -Pane::zoomToRegion(int x0, int y0, int x1, int y1) +Pane::zoomToRegion(QRect r) { + int x0 = r.x(); + int y0 = r.y(); + int x1 = r.x() + r.width(); + int y1 = r.y() + r.height(); + int w = x1 - x0; - - long newStartFrame = getFrameForX(x0); - - long visibleFrames = getEndFrame() - getStartFrame(); + + sv_frame_t newStartFrame = getFrameForX(x0); + + sv_frame_t visibleFrames = getEndFrame() - getStartFrame(); if (newStartFrame <= -visibleFrames) { newStartFrame = -visibleFrames + 1; } - - if (newStartFrame >= long(getModelsEndFrame())) { + + if (newStartFrame >= getModelsEndFrame()) { newStartFrame = getModelsEndFrame() - 1; } - - float ratio = float(w) / float(width()); + + double ratio = double(w) / double(width()); // cerr << "ratio: " << ratio << endl; - size_t newZoomLevel = (size_t)nearbyint(m_zoomLevel * ratio); + int newZoomLevel = (int)nearbyint(m_zoomLevel * ratio); if (newZoomLevel < 1) newZoomLevel = 1; // cerr << "start: " << m_startFrame << ", level " << m_zoomLevel << endl; @@ -1712,11 +1877,11 @@ setStartFrame(newStartFrame); QString unit; - float min, max; + double min, max; bool log; Layer *layer = 0; - for (LayerList::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { if ((*i)->getValueExtents(min, max, log, unit) && (*i)->getDisplayExtents(min, max)) { layer = *i; @@ -1726,15 +1891,15 @@ if (layer) { if (log) { - min = (min < 0.0) ? -log10f(-min) : (min == 0.0) ? 0.0 : log10f(min); - max = (max < 0.0) ? -log10f(-max) : (max == 0.0) ? 0.0 : log10f(max); + min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min); + max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max); } - float rmin = min + ((max - min) * (height() - y1)) / height(); - float rmax = min + ((max - min) * (height() - y0)) / height(); + double rmin = min + ((max - min) * (height() - y1)) / height(); + double rmax = min + ((max - min) * (height() - y0)) / height(); cerr << "min: " << min << ", max: " << max << ", y0: " << y0 << ", y1: " << y1 << ", h: " << height() << ", rmin: " << rmin << ", rmax: " << rmax << endl; if (log) { - rmin = powf(10, rmin); - rmax = powf(10, rmax); + rmin = pow(10, rmin); + rmax = pow(10, rmax); } cerr << "finally: rmin: " << rmin << ", rmax: " << rmax << " " << unit << endl; @@ -1783,19 +1948,18 @@ if (m_dragMode == HorizontalDrag || m_dragMode == FreeDrag) { - long frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x()); - - size_t newCentreFrame = m_dragCentreFrame; - + sv_frame_t frameOff = getFrameForX(e->x()) - getFrameForX(m_clickPos.x()); + sv_frame_t newCentreFrame = m_dragCentreFrame; + if (frameOff < 0) { newCentreFrame -= frameOff; - } else if (newCentreFrame >= size_t(frameOff)) { + } else if (newCentreFrame >= frameOff) { newCentreFrame -= frameOff; } else { newCentreFrame = 0; } -#ifdef DEBUG_PANE +#ifdef DEBUG_PANE SVDEBUG << "Pane::dragTopLayer: newCentreFrame = " << newCentreFrame << ", models end frame = " << getModelsEndFrame() << endl; #endif @@ -1813,24 +1977,24 @@ if (m_dragMode == VerticalDrag || m_dragMode == FreeDrag) { - float vmin = 0.f, vmax = 0.f; - float dmin = 0.f, dmax = 0.f; + double vmin = 0.f, vmax = 0.f; + double dmin = 0.f, dmax = 0.f; if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) { // cerr << "ydiff = " << ydiff << endl; int ydiff = e->y() - m_clickPos.y(); - float perpix = (dmax - dmin) / height(); - float valdiff = ydiff * perpix; + double perpix = (dmax - dmin) / height(); + double valdiff = ydiff * perpix; // cerr << "valdiff = " << valdiff << endl; if (m_dragMode == UnresolvedDrag && ydiff != 0) { m_dragMode = VerticalDrag; } - float newmin = m_dragStartMinValue + valdiff; - float newmax = m_dragStartMinValue + (dmax - dmin) + valdiff; + double newmin = m_dragStartMinValue + valdiff; + double newmax = m_dragStartMinValue + (dmax - dmin) + valdiff; if (newmin < vmin) { newmax += vmin - newmin; newmin += vmin - newmin; @@ -1862,6 +2026,11 @@ int smallThreshold = 10, bigThreshold = 80; + if (m_manager) { + smallThreshold = m_manager->scalePixelSize(smallThreshold); + bigThreshold = m_manager->scalePixelSize(bigThreshold); + } + // SVDEBUG << "Pane::updateDragMode: xdiff = " << xdiff << ", ydiff = " // << ydiff << ", canMoveVertical = " << canMoveVertical << ", drag mode = " << m_dragMode << endl; @@ -1910,13 +2079,14 @@ void Pane::dragExtendSelection(QMouseEvent *e) { - int mouseFrame = getFrameForX(e->x()); - size_t resolution = 1; - int snapFrameLeft = mouseFrame; - int snapFrameRight = mouseFrame; - - Layer *layer = getSelectedLayer(); - if (layer && !m_shiftPressed) { + sv_frame_t mouseFrame = getFrameForX(e->x()); + int resolution = 1; + sv_frame_t snapFrameLeft = mouseFrame; + sv_frame_t snapFrameRight = mouseFrame; + + Layer *layer = getInteractionLayer(); + if (layer && !m_shiftPressed && + !qobject_cast(layer)) { // don't snap to secs layer->snapToFeatureFrame(this, snapFrameLeft, resolution, Layer::SnapLeft); layer->snapToFeatureFrame(this, snapFrameRight, @@ -1927,13 +2097,13 @@ if (snapFrameLeft < 0) snapFrameLeft = 0; if (snapFrameRight < 0) snapFrameRight = 0; - - size_t min, max; - - if (m_selectionStartFrame > size_t(snapFrameLeft)) { + + sv_frame_t min, max; + + if (m_selectionStartFrame > snapFrameLeft) { min = snapFrameLeft; max = m_selectionStartFrame; - } else if (size_t(snapFrameRight) > m_selectionStartFrame) { + } else if (snapFrameRight > m_selectionStartFrame) { min = m_selectionStartFrame; max = snapFrameRight; } else { @@ -1941,36 +2111,53 @@ max = snapFrameRight; } + sv_frame_t end = getModelsEndFrame(); + if (min > end) min = end; + if (max > end) max = end; + if (m_manager) { - m_manager->setInProgressSelection(Selection(alignToReference(min), - alignToReference(max)), - !m_resizing && !m_ctrlPressed); + + Selection sel(alignToReference(min), alignToReference(max)); + + bool exc; + bool same = (m_manager->haveInProgressSelection() && + m_manager->getInProgressSelection(exc) == sel); + + m_manager->setInProgressSelection(sel, !m_resizing && !m_ctrlPressed); + + if (!same) { + edgeScrollMaybe(e->x()); + } } - edgeScrollMaybe(e->x()); - update(); + + if (min != max) { + m_playbackFrameMoveScheduled = false; + } } void Pane::edgeScrollMaybe(int x) { - int mouseFrame = getFrameForX(x); + sv_frame_t mouseFrame = getFrameForX(x); bool doScroll = false; if (!m_manager) doScroll = true; - if (!m_manager->isPlaying()) doScroll = true; + else if (!m_manager->isPlaying()) doScroll = true; + if (m_followPlay != PlaybackScrollContinuous) doScroll = true; if (doScroll) { - int offset = mouseFrame - getStartFrame(); - int available = getEndFrame() - getStartFrame(); - int move = 0; - if (offset >= available * 0.95) { - move = int(offset - available * 0.95) + 1; - } else if (offset <= available * 0.10) { - move = int(available * 0.10 - offset) + 1; - move = -move; + sv_frame_t offset = mouseFrame - getStartFrame(); + sv_frame_t available = getEndFrame() - getStartFrame(); + sv_frame_t move = 0; + sv_frame_t rightEdge = available - (available / 20); + sv_frame_t leftEdge = (available / 10); + if (offset >= rightEdge) { + move = offset - rightEdge + 1; + } else if (offset <= leftEdge) { + move = offset - leftEdge - 1; } if (move != 0) { setCentreFrame(m_centreFrame + move); @@ -1986,7 +2173,7 @@ return; } -// cerr << "mouseDoubleClickEvent" << endl; + cerr << "mouseDoubleClickEvent" << endl; m_clickPos = e->pos(); m_clickedInRange = true; @@ -1994,19 +2181,29 @@ m_ctrlPressed = (e->modifiers() & Qt::ControlModifier); m_altPressed = (e->modifiers() & Qt::AltModifier); + // cancel any pending move that came from a single click + m_playbackFrameMoveScheduled = false; + ViewManager::ToolMode mode = ViewManager::NavigateMode; - if (m_manager) mode = m_manager->getToolMode(); + if (m_manager) mode = m_manager->getToolModeFor(this); bool relocate = (mode == ViewManager::NavigateMode || (e->buttons() & Qt::MidButton)); + if (mode == ViewManager::SelectMode) { + m_clickedInRange = false; + if (m_manager) m_manager->clearInProgressSelection(); + emit doubleClickSelectInvoked(getFrameForX(e->x())); + return; + } + if (mode == ViewManager::NavigateMode || mode == ViewManager::EditMode) { - Layer *layer = getSelectedLayer(); - if (layer && layer->isLayerEditable()) { - if (layer->editOpen(this, e)) relocate = false; - } + Layer *layer = getInteractionLayer(); + if (layer && layer->isLayerEditable()) { + if (layer->editOpen(this, e)) relocate = false; + } } else if (mode == ViewManager::MeasureMode) { @@ -2017,7 +2214,7 @@ if (relocate) { - long f = getFrameForX(e->x()); + sv_frame_t f = getFrameForX(e->x()); setCentreFrame(f); @@ -2025,11 +2222,19 @@ m_dragStartMinValue = 0; m_dragMode = UnresolvedDrag; - float vmin, vmax, dmin, dmax; + double vmin, vmax, dmin, dmax; if (getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) { m_dragStartMinValue = dmin; } } + + if (mode == ViewManager::NoteEditMode) { + std::cerr << "double click in note edit mode" << std::endl; + Layer *layer = getInteractionLayer(); + if (layer && layer->isLayerEditable()) { + layer->addNote(this, e); + } + } m_clickedInRange = false; // in case mouseReleaseEvent is not properly called } @@ -2059,78 +2264,145 @@ void Pane::wheelEvent(QWheelEvent *e) { - //cerr << "wheelEvent, delta " << e->delta() << endl; - - int count = e->delta(); - - if (count > 0) { - if (count >= 120) count /= 120; - else count = 1; - } - - if (count < 0) { - if (count <= -120) count /= 120; - else count = -1; + cerr << "wheelEvent, delta " << e->delta() << ", angleDelta " << e->angleDelta().x() << "," << e->angleDelta().y() << ", pixelDelta " << e->pixelDelta().x() << "," << e->pixelDelta().y() << ", modifiers " << e->modifiers() << endl; + + int dx = e->angleDelta().x(); + int dy = e->angleDelta().y(); + + if (dx == 0 && dy == 0) { + return; } - if (e->modifiers() & Qt::ControlModifier) { - - // Scroll left or right, rapidly - - if (getStartFrame() < 0 && - getEndFrame() >= getModelsEndFrame()) return; - - long delta = ((width() / 2) * count * m_zoomLevel); - - if (int(m_centreFrame) < delta) { - setCentreFrame(0); - } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { - setCentreFrame(getModelsEndFrame()); - } else { - setCentreFrame(m_centreFrame - delta); - } - - } else if (e->modifiers() & Qt::ShiftModifier) { + int d = dy; + bool horizontal = false; + + if (abs(dx) > abs(dy)) { + d = dx; + horizontal = true; + } else if (e->modifiers() & Qt::ControlModifier) { + // treat a vertical wheel as horizontal + horizontal = true; + } + + if (e->phase() == Qt::ScrollBegin || + fabs(d) >= 120 || + (d > 0 && m_pendingWheelAngle < 0) || + (d < 0 && m_pendingWheelAngle > 0)) { + m_pendingWheelAngle = d; + } else { + m_pendingWheelAngle += d; + } + + if (horizontal && e->pixelDelta().x() != 0) { + + // Have fine pixel information: use it + + wheelHorizontalFine(e->pixelDelta().x(), e->modifiers()); + + m_pendingWheelAngle = 0; + + } else { + + // Coarse wheel information (or vertical zoom, which is + // necessarily coarse itself) + + // Sometimes on Linux we're seeing absurdly extreme angles on + // the first wheel event -- discard those entirely + if (abs(m_pendingWheelAngle) >= 600) { + m_pendingWheelAngle = 0; + return; + } + + while (abs(m_pendingWheelAngle) >= 120) { + + int sign = (m_pendingWheelAngle < 0 ? -1 : 1); + + if (horizontal) { + wheelHorizontal(sign, e->modifiers()); + } else { + wheelVertical(sign, e->modifiers()); + } + + m_pendingWheelAngle -= sign * 120; + } + } +} + +void +Pane::wheelVertical(int sign, Qt::KeyboardModifiers mods) +{ + cerr << "wheelVertical: sign = " << sign << endl; + + if (mods & Qt::ShiftModifier) { + + // Pan vertically + + if (m_vpan) { + m_vpan->scroll(sign > 0); + } + + } else if (mods & Qt::AltModifier) { // Zoom vertically - if (m_vpan) { - m_vpan->scroll(e->delta() > 0); + if (m_vthumb) { + m_vthumb->scroll(sign > 0); } - } else if (e->modifiers() & Qt::AltModifier) { - - // Zoom vertically - - if (m_vthumb) { - m_vthumb->scroll(e->delta() > 0); + } else { + + // Zoom in or out + + int newZoomLevel = m_zoomLevel; + + if (sign > 0) { + if (newZoomLevel <= 2) { + newZoomLevel = 1; + } else { + newZoomLevel = getZoomConstraintBlockSize + (newZoomLevel - 1, ZoomConstraint::RoundDown); + } + } else { // sign < 0 + newZoomLevel = getZoomConstraintBlockSize + (newZoomLevel + 1, ZoomConstraint::RoundUp); } - + + if (newZoomLevel != m_zoomLevel) { + setZoomLevel(newZoomLevel); + } + } + + emit paneInteractedWith(); +} + +void +Pane::wheelHorizontal(int sign, Qt::KeyboardModifiers mods) +{ + cerr << "wheelHorizontal: sign = " << sign << endl; + + // Scroll left or right, rapidly + + wheelHorizontalFine((width() / 4) * sign, mods); +} + +void +Pane::wheelHorizontalFine(int pixels, Qt::KeyboardModifiers) +{ + cerr << "wheelHorizontalFine: pixels = " << pixels << endl; + + // Scroll left or right by a fixed number of pixels + + if (getStartFrame() < 0 && + getEndFrame() >= getModelsEndFrame()) return; + + int delta = (pixels * m_zoomLevel); + + if (m_centreFrame < delta) { + setCentreFrame(0); + } else if (m_centreFrame - delta >= getModelsEndFrame()) { + setCentreFrame(getModelsEndFrame()); } else { - - // Zoom in or out - - int newZoomLevel = m_zoomLevel; - - while (count > 0) { - if (newZoomLevel <= 2) { - newZoomLevel = 1; - break; - } - newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, - ZoomConstraint::RoundDown); - --count; - } - - while (count < 0) { - newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, - ZoomConstraint::RoundUp); - ++count; - } - - if (newZoomLevel != m_zoomLevel) { - setZoomLevel(newZoomLevel); - } + setCentreFrame(m_centreFrame - delta); } emit paneInteractedWith(); @@ -2147,7 +2419,7 @@ //!!! pull out into function (presumably in View) bool haveConstraint = false; - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) { haveConstraint = true; @@ -2209,13 +2481,13 @@ } void -Pane::verticalPannerMoved(float x0, float y0, float w, float h) +Pane::verticalPannerMoved(float , float y0, float , float h) { - float vmin, vmax, dmin, dmax; + double vmin, vmax, dmin, dmax; if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax)) return; - float y1 = y0 + h; - float newmax = vmin + ((1.0 - y0) * (vmax - vmin)); - float newmin = vmin + ((1.0 - y1) * (vmax - vmin)); + double y1 = y0 + h; + double newmax = vmin + ((1.0 - y0) * (vmax - vmin)); + double newmin = vmin + ((1.0 - y1) * (vmax - vmin)); // cerr << "verticalPannerMoved: (" << x0 << "," << y0 << "," << w // << "," << h << ") -> (" << newmin << "," << newmax << ")" << endl; setTopLayerDisplayExtents(newmin, newmax); @@ -2226,7 +2498,7 @@ { if (!m_vpan || !m_manager || !m_manager->getZoomWheelsEnabled()) return; - float vmin, vmax, dmin, dmax; + double vmin, vmax, dmin, dmax; QString unit; if (!getTopLayerDisplayExtents(vmin, vmax, dmin, dmax, &unit) || vmax == vmin) { @@ -2236,12 +2508,13 @@ RangeInputDialog dialog(tr("Enter new range"), tr("New vertical display range, from %1 to %2 %4:") .arg(vmin).arg(vmax).arg(unit), - unit, vmin, vmax, this); - dialog.setRange(dmin, dmax); + unit, float(vmin), float(vmax), this); + dialog.setRange(float(dmin), float(dmax)); if (dialog.exec() == QDialog::Accepted) { - dialog.getRange(dmin, dmax); - setTopLayerDisplayExtents(dmin, dmax); + float newmin, newmax; + dialog.getRange(newmin, newmax); + setTopLayerDisplayExtents(newmin, newmax); updateVerticalPanner(); } } @@ -2308,9 +2581,9 @@ Pane::editSelectionStart(QMouseEvent *e) { if (!m_identifyFeatures || - !m_manager || - m_manager->getToolMode() != ViewManager::EditMode) { - return false; + !m_manager || + m_manager->getToolModeFor(this) != ViewManager::EditMode) { + return false; } bool closeToLeft, closeToRight; @@ -2337,41 +2610,41 @@ if (m_editingSelection.isEmpty()) return false; int offset = m_mousePos.x() - m_clickPos.x(); - Layer *layer = getSelectedLayer(); + Layer *layer = getInteractionLayer(); if (offset == 0 || !layer) { - m_editingSelection = Selection(); - return true; + m_editingSelection = Selection(); + return true; } int p0 = getXForFrame(m_editingSelection.getStartFrame()) + offset; int p1 = getXForFrame(m_editingSelection.getEndFrame()) + offset; - long f0 = getFrameForX(p0); - long f1 = getFrameForX(p1); + sv_frame_t f0 = getFrameForX(p0); + sv_frame_t f1 = getFrameForX(p1); Selection newSelection(f0, f1); if (m_editingSelectionEdge == 0) { - + CommandHistory::getInstance()->startCompoundOperation (tr("Drag Selection"), true); - layer->moveSelection(m_editingSelection, f0); - + layer->moveSelection(m_editingSelection, f0); + } else { - + CommandHistory::getInstance()->startCompoundOperation (tr("Resize Selection"), true); - if (m_editingSelectionEdge < 0) { - f1 = m_editingSelection.getEndFrame(); - } else { - f0 = m_editingSelection.getStartFrame(); - } - - newSelection = Selection(f0, f1); - layer->resizeSelection(m_editingSelection, newSelection); + if (m_editingSelectionEdge < 0) { + f1 = m_editingSelection.getEndFrame(); + } else { + f0 = m_editingSelection.getStartFrame(); + } + + newSelection = Selection(f0, f1); + layer->resizeSelection(m_editingSelection, newSelection); } m_manager->removeSelection(m_editingSelection); @@ -2386,7 +2659,7 @@ void Pane::toolModeChanged() { - ViewManager::ToolMode mode = m_manager->getToolMode(); + ViewManager::ToolMode mode = m_manager->getToolModeFor(this); // SVDEBUG << "Pane::toolModeChanged(" << mode << ")" << endl; if (mode == ViewManager::MeasureMode && !m_measureCursor1) { @@ -2401,33 +2674,38 @@ switch (mode) { case ViewManager::NavigateMode: - setCursor(Qt::PointingHandCursor); - break; - + setCursor(Qt::PointingHandCursor); + break; + case ViewManager::SelectMode: - setCursor(Qt::ArrowCursor); - break; - + setCursor(Qt::ArrowCursor); + break; + case ViewManager::EditMode: - setCursor(Qt::UpArrowCursor); - break; - + setCursor(Qt::UpArrowCursor); + break; + case ViewManager::DrawMode: - setCursor(Qt::CrossCursor); - break; - + setCursor(Qt::CrossCursor); + break; + case ViewManager::EraseMode: - setCursor(Qt::CrossCursor); - break; + setCursor(Qt::CrossCursor); + break; case ViewManager::MeasureMode: if (m_measureCursor1) setCursor(*m_measureCursor1); - break; - -/* + break; + + // GF: NoteEditMode uses the same default cursor as EditMode, but it will change in a context sensitive manner. + case ViewManager::NoteEditMode: + setCursor(Qt::UpArrowCursor); + break; + +/* case ViewManager::TextMode: - setCursor(Qt::IBeamCursor); - break; + setCursor(Qt::IBeamCursor); + break; */ } } @@ -2440,7 +2718,7 @@ } void -Pane::viewZoomLevelChanged(View *v, unsigned long z, bool locked) +Pane::viewZoomLevelChanged(View *v, int z, bool locked) { // cerr << "Pane[" << this << "]::zoomLevelChanged (global now " // << (m_manager ? m_manager->getGlobalZoom() : 0) << ")" << endl; @@ -2511,10 +2789,10 @@ } ViewManager::ToolMode mode = ViewManager::NavigateMode; - if (m_manager) mode = m_manager->getToolMode(); + if (m_manager) mode = m_manager->getToolModeFor(this); bool editable = false; - Layer *layer = getSelectedLayer(); + Layer *layer = getInteractionLayer(); if (layer && layer->isLayerEditable()) { editable = true; } @@ -2563,21 +2841,21 @@ } else if (mode == ViewManager::DrawMode) { //!!! could call through to a layer function to find out exact meaning - if (editable) { + if (editable) { help = tr("Click to add a new item in the active layer"); } } else if (mode == ViewManager::EraseMode) { //!!! could call through to a layer function to find out exact meaning - if (editable) { + if (editable) { help = tr("Click to erase an item from the active layer"); } } else if (mode == ViewManager::EditMode) { //!!! could call through to layer - if (editable) { + if (editable) { help = tr("Click and drag an item in the active layer to move it; hold Shift to override initial resistance"); if (pos) { bool closeToLeft = false, closeToRight = false; @@ -2621,8 +2899,8 @@ { View::toXml (stream, indent, - QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3") - .arg(m_centreLineVisible).arg(height()).arg(extraAttributes)); + QString("type=\"pane\" centreLineVisible=\"%1\" height=\"%2\" %3") + .arg(m_centreLineVisible).arg(height()).arg(extraAttributes)); } diff -r 282f4be8f058 -r c02c51ae5238 view/Pane.h --- a/view/Pane.h Tue Jul 14 15:04:46 2015 +0100 +++ b/view/Pane.h Wed Apr 20 12:06:28 2016 +0100 @@ -48,13 +48,13 @@ void setCentreLineVisible(bool visible); bool getCentreLineVisible() const { return m_centreLineVisible; } - virtual size_t getFirstVisibleFrame() const; + virtual sv_frame_t getFirstVisibleFrame() const; - virtual size_t getVerticalScaleWidth() const; + virtual int getVerticalScaleWidth() const; - virtual QImage *toNewImage(size_t f0, size_t f1); + virtual QImage *toNewImage(sv_frame_t f0, sv_frame_t f1); virtual QImage *toNewImage() { return View::toNewImage(); } - virtual QSize getImageSize(size_t f0, size_t f1); + virtual QSize getImageSize(sv_frame_t f0, sv_frame_t f1); virtual QSize getImageSize() { return View::getImageSize(); } virtual void toXml(QTextStream &stream, QString indent = "", @@ -62,16 +62,24 @@ static void registerShortcuts(KeyReference &kr); + enum PaneType { + Normal = 0, + TonyMain = 1, + TonySelection = 2 + }; + signals: void paneInteractedWith(); void rightButtonMenuRequested(QPoint position); void dropAccepted(QStringList uriList); void dropAccepted(QString text); + void doubleClickSelectInvoked(sv_frame_t frame); + void regionOutlined(QRect rect); public slots: virtual void toolModeChanged(); virtual void zoomWheelsEnabledChanged(); - virtual void viewZoomLevelChanged(View *v, unsigned long z, bool locked); + virtual void viewZoomLevelChanged(View *v, int z, bool locked); virtual void modelAlignmentCompletionChanged(); virtual void horizontalThumbwheelMoved(int value); @@ -84,9 +92,14 @@ virtual void propertyContainerSelected(View *, PropertyContainer *pc); + void zoomToRegion(QRect r); + void mouseEnteredWidget(); void mouseLeftWidget(); +protected slots: + void playbackScheduleTimerElapsed(); + protected: virtual void paintEvent(QPaintEvent *e); virtual void mousePressEvent(QMouseEvent *e); @@ -100,16 +113,21 @@ virtual void dragEnterEvent(QDragEnterEvent *e); virtual void dropEvent(QDropEvent *e); + void wheelVertical(int sign, Qt::KeyboardModifiers); + void wheelHorizontal(int sign, Qt::KeyboardModifiers); + void wheelHorizontalFine(int pixels, Qt::KeyboardModifiers); + void drawVerticalScale(QRect r, Layer *, QPainter &); void drawFeatureDescription(Layer *, QPainter &); - void drawCentreLine(int, QPainter &, bool omitLine); - void drawDurationAndRate(QRect, const Model *, int, QPainter &); + void drawCentreLine(sv_samplerate_t, QPainter &, bool omitLine); + void drawModelTimeExtents(QRect, QPainter &, const Model *); + void drawDurationAndRate(QRect, const Model *, sv_samplerate_t, QPainter &); void drawWorkTitle(QRect, QPainter &, const Model *); void drawLayerNames(QRect, QPainter &); void drawEditingSelection(QPainter &); void drawAlignmentStatus(QRect, QPainter &, const Model *, bool down); - virtual bool render(QPainter &paint, int x0, size_t f0, size_t f1); + virtual bool render(QPainter &paint, int x0, sv_frame_t f0, sv_frame_t f1); Selection getSelectionAt(int x, bool &closeToLeft, bool &closeToRight) const; @@ -122,17 +140,20 @@ void updateVerticalPanner(); bool canTopLayerMoveVertical(); - bool getTopLayerDisplayExtents(float &valueMin, float &valueMax, - float &displayMin, float &displayMax, + bool getTopLayerDisplayExtents(double &valueMin, double &valueMax, + double &displayMin, double &displayMax, QString *unit = 0); - bool setTopLayerDisplayExtents(float displayMin, float displayMax); + bool setTopLayerDisplayExtents(double displayMin, double displayMax); void dragTopLayer(QMouseEvent *e); void dragExtendSelection(QMouseEvent *e); - void zoomToRegion(int x0, int y0, int x1, int y1); void updateContextHelp(const QPoint *pos); void edgeScrollMaybe(int x); + Layer *getTopFlexiNoteLayer(); + + void schedulePlaybackFrameMove(sv_frame_t frame); + bool m_identifyFeatures; QPoint m_identifyPoint; QPoint m_clickPos; @@ -146,14 +167,16 @@ bool m_resizing; bool m_editing; bool m_releasing; - size_t m_dragCentreFrame; - float m_dragStartMinValue; + sv_frame_t m_dragCentreFrame; + double m_dragStartMinValue; bool m_centreLineVisible; - size_t m_selectionStartFrame; + sv_frame_t m_selectionStartFrame; Selection m_editingSelection; int m_editingSelectionEdge; mutable int m_scaleWidth; + int m_pendingWheelAngle; + enum DragMode { UnresolvedDrag, VerticalDrag, @@ -178,6 +201,9 @@ bool m_mouseInWidget; + bool m_playbackFrameMoveScheduled; + sv_frame_t m_playbackFrameMoveTo; + static QCursor *m_measureCursor1; static QCursor *m_measureCursor2; }; diff -r 282f4be8f058 -r c02c51ae5238 view/PaneStack.cpp --- a/view/PaneStack.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/view/PaneStack.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -39,6 +39,7 @@ PaneStack::PaneStack(QWidget *parent, ViewManager *viewManager) : QFrame(parent), m_currentPane(0), + m_showAccessories(true), m_splitter(new QSplitter), m_propertyStackStack(new QStackedWidget), m_viewManager(viewManager), @@ -60,6 +61,12 @@ setLayout(layout); } +void +PaneStack::setShowPaneAccessories(bool show) +{ + m_showAccessories = show; +} + Pane * PaneStack::addPane(bool suppressPropertyBox) { @@ -79,6 +86,7 @@ xButton->setIcon(IconLoader().load("cross")); xButton->setFixedSize(QSize(16, 16)); xButton->setFlat(true); + xButton->setVisible(m_showAccessories); layout->addWidget(xButton, 0, 0); connect(xButton, SIGNAL(clicked()), this, SLOT(paneDeleteButtonClicked())); @@ -88,12 +96,11 @@ layout->setRowStretch(1, 20); currentIndicator->setMinimumWidth(8); currentIndicator->setScaledContents(true); + currentIndicator->setVisible(m_showAccessories); - long initialCentreFrame = -1; - for (int i = 0; i < m_panes.size(); ++i) { - long f = m_panes[i].pane->getCentreFrame(); - initialCentreFrame = f; - break; + sv_frame_t initialCentreFrame = -1; + if (!m_panes.empty()) { + initialCentreFrame = m_panes[0].pane->getCentreFrame(); } Pane *pane = new Pane(frame); @@ -149,6 +156,8 @@ this, SLOT(paneDropAccepted(QStringList))); connect(pane, SIGNAL(dropAccepted(QString)), this, SLOT(paneDropAccepted(QString))); + connect(pane, SIGNAL(doubleClickSelectInvoked(sv_frame_t)), + this, SIGNAL(doubleClickSelectInvoked(sv_frame_t))); emit paneAdded(pane); emit paneAdded(); @@ -209,7 +218,7 @@ Pane * PaneStack::getPane(int n) { - if (n < m_panes.size()) { + if (n < (int)m_panes.size()) { return m_panes[n].pane; } else { return 0; @@ -236,11 +245,16 @@ void PaneStack::deletePane(Pane *pane) { + cerr << "PaneStack::deletePane(" << pane << ")" << endl; + std::vector::iterator i; bool found = false; + QWidget *stack = 0; + for (i = m_panes.begin(); i != m_panes.end(); ++i) { if (i->pane == pane) { + stack = i->propertyStack; m_panes.erase(i); found = true; break; @@ -251,6 +265,7 @@ for (i = m_hiddenPanes.begin(); i != m_hiddenPanes.end(); ++i) { if (i->pane == pane) { + stack = i->propertyStack; m_hiddenPanes.erase(i); found = true; break; @@ -265,6 +280,18 @@ emit paneAboutToBeDeleted(pane); + cerr << "PaneStack::deletePane: about to delete parent " << pane->parent() << " of pane " << pane << endl; + + // The property stack associated with the parent was initially + // created with the same parent as it, so it would be deleted when + // we delete the pane's parent in a moment -- but it may have been + // reparented depending on the layout. We'd better delete it + // separately first. (This fixes a crash on opening a new layer + // with a new unit type in it, when a long-defunct property box + // could be signalled from the unit database to tell it that a new + // unit had appeared.) + delete stack; + delete pane->parent(); if (m_currentPane == pane) { @@ -288,21 +315,21 @@ bool multi = (getPaneCount() > 1); for (std::vector::iterator i = m_panes.begin(); i != m_panes.end(); ++i) { - i->xButton->setVisible(multi); - i->currentIndicator->setVisible(multi); + i->xButton->setVisible(multi && m_showAccessories); + i->currentIndicator->setVisible(multi && m_showAccessories); } } int PaneStack::getPaneCount() const { - return m_panes.size(); + return int(m_panes.size()); } int PaneStack::getHiddenPaneCount() const { - return m_hiddenPanes.size(); + return int(m_hiddenPanes.size()); } void @@ -503,7 +530,7 @@ if (m_propertyStackMinWidth > 0) maxMinWidth = m_propertyStackMinWidth; - for (size_t i = 0; i < m_panes.size(); ++i) { + for (int i = 0; i < (int)m_panes.size(); ++i) { if (!m_panes[i].propertyStack) continue; #ifdef DEBUG_PANE_STACK SVDEBUG << "PaneStack::sizePropertyStacks: " << i << ": min " @@ -525,7 +552,7 @@ m_propertyStackStack->setMaximumWidth(setWidth + 10); - for (size_t i = 0; i < m_panes.size(); ++i) { + for (int i = 0; i < (int)m_panes.size(); ++i) { if (!m_panes[i].propertyStack) continue; m_panes[i].propertyStack->setMinimumWidth(setWidth); } @@ -552,7 +579,7 @@ PaneStack::paneDeleteButtonClicked() { QObject *s = sender(); - for (size_t i = 0; i < m_panes.size(); ++i) { + for (int i = 0; i < (int)m_panes.size(); ++i) { if (m_panes[i].xButton == s) { emit paneDeleteButtonClicked(m_panes[i].pane); } @@ -564,7 +591,7 @@ { QObject *s = sender(); - for (size_t i = 0; i < m_panes.size(); ++i) { + for (int i = 0; i < (int)m_panes.size(); ++i) { if (m_panes[i].currentIndicator == s) { setCurrentPane(m_panes[i].pane); return; @@ -580,24 +607,44 @@ int count = sizes.size(); - int total = 0; + int fixed = 0, variable = 0, total = 0; + int varicount = 0; + for (int i = 0; i < count; ++i) { total += sizes[i]; } + variable = total; + + for (int i = 0; i < count; ++i) { + int minh = m_panes[i].pane->minimumSize().height(); + if (minh == m_panes[i].pane->maximumSize().height()) { + fixed += minh; + variable -= minh; + } else { + varicount++; + } + } + if (total == 0) return; sizes.clear(); - int each = total / count; + int each = (varicount > 0 ? (variable / varicount) : 0); int remaining = total; for (int i = 0; i < count; ++i) { if (i == count - 1) { sizes.push_back(remaining); } else { - sizes.push_back(each); - remaining -= each; + int minh = m_panes[i].pane->minimumSize().height(); + if (minh == m_panes[i].pane->maximumSize().height()) { + sizes.push_back(minh); + remaining -= minh; + } else { + sizes.push_back(each); + remaining -= each; + } } } @@ -612,4 +659,3 @@ m_splitter->setSizes(sizes); } - diff -r 282f4be8f058 -r c02c51ae5238 view/PaneStack.h --- a/view/PaneStack.h Tue Jul 14 15:04:46 2015 +0100 +++ b/view/PaneStack.h Wed Apr 20 12:06:28 2016 +0100 @@ -20,6 +20,8 @@ #include +#include "base/BaseTypes.h" + class QWidget; class QLabel; class QStackedWidget; @@ -68,6 +70,8 @@ void setLayoutStyle(LayoutStyle style); void setPropertyStackMinWidth(int mw); + + void setShowPaneAccessories(bool show); // current indicator, close button void sizePanesEqually(); @@ -91,6 +95,8 @@ void paneDeleteButtonClicked(Pane *pane); + void doubleClickSelectInvoked(sv_frame_t frame); + public slots: void propertyContainerAdded(PropertyContainer *); void propertyContainerRemoved(PropertyContainer *); @@ -119,6 +125,8 @@ std::vector m_panes; std::vector m_hiddenPanes; + bool m_showAccessories; + QSplitter *m_splitter; QStackedWidget *m_propertyStackStack; diff -r 282f4be8f058 -r c02c51ae5238 view/View.cpp --- a/view/View.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/view/View.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -20,12 +20,15 @@ #include "base/Profiler.h" #include "base/Pitch.h" #include "base/Preferences.h" +#include "ViewProxy.h" #include "layer/TimeRulerLayer.h" #include "layer/SingleColourLayer.h" #include "data/model/PowerOfSqrtTwoZoomConstraint.h" #include "data/model/RangeSummarisableTimeValueModel.h" +#include "widgets/IconLoader.h" + #include #include #include @@ -34,6 +37,8 @@ #include #include #include +#include +#include #include #include @@ -41,21 +46,22 @@ #include +//#define DEBUG_VIEW 1 //#define DEBUG_VIEW_WIDGET_PAINT 1 - - - View::View(QWidget *w, bool showProgress) : QFrame(w), + m_id(getNextId()), m_centreFrame(0), m_zoomLevel(1024), m_followPan(true), m_followZoom(true), - m_followPlay(PlaybackScrollPage), + m_followPlay(PlaybackScrollPageWithCentre), + m_followPlayIsDetached(false), m_playPointerFrame(0), m_showProgress(showProgress), m_cache(0), + m_buffer(0), m_cacheCentreFrame(0), m_cacheZoomLevel(1024), m_selectionCached(false), @@ -64,12 +70,12 @@ m_manager(0), m_propertyContainer(new ViewPropertyContainer(this)) { - SVDEBUG << "View::View(" << this << ")" << endl; +// cerr << "View::View(" << this << ")" << endl; } View::~View() { -// SVDEBUG << "View::~View(" << this << ")" << endl; +// cerr << "View::~View(" << this << ")" << endl; m_deleting = true; delete m_propertyContainer; @@ -113,8 +119,12 @@ if (name == "Follow Playback") { if (min) *min = 0; if (max) *max = 2; - if (deflt) *deflt = int(PlaybackScrollPage); - return int(m_followPlay); + if (deflt) *deflt = int(PlaybackScrollPageWithCentre); + switch (m_followPlay) { + case PlaybackScrollContinuous: return 0; + case PlaybackScrollPageWithCentre: case PlaybackScrollPage: return 1; + case PlaybackIgnore: return 2; + } } if (min) *min = 0; if (max) *max = 0; @@ -148,43 +158,43 @@ switch (value) { default: case 0: setPlaybackFollow(PlaybackScrollContinuous); break; - case 1: setPlaybackFollow(PlaybackScrollPage); break; + case 1: setPlaybackFollow(PlaybackScrollPageWithCentre); break; case 2: setPlaybackFollow(PlaybackIgnore); break; } } } -size_t +int View::getPropertyContainerCount() const { - return m_layers.size() + 1; // the 1 is for me + return int(m_fixedOrderLayers.size()) + 1; // the 1 is for me } const PropertyContainer * -View::getPropertyContainer(size_t i) const +View::getPropertyContainer(int i) const { return (const PropertyContainer *)(((View *)this)-> getPropertyContainer(i)); } PropertyContainer * -View::getPropertyContainer(size_t i) +View::getPropertyContainer(int i) { if (i == 0) return m_propertyContainer; - return m_layers[i-1]; + return m_fixedOrderLayers[i-1]; } bool -View::getValueExtents(QString unit, float &min, float &max, bool &log) const +View::getValueExtents(QString unit, double &min, double &max, bool &log) const { bool have = false; - for (LayerList::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { QString layerUnit; - float layerMin = 0.0, layerMax = 0.0; - float displayMin = 0.0, displayMax = 0.0; + double layerMin = 0.0, layerMax = 0.0; + double displayMin = 0.0, displayMax = 0.0; bool layerLog = false; if ((*i)->getValueExtents(layerMin, layerMax, layerLog, layerUnit) && @@ -216,8 +226,8 @@ { std::map sortedLayers; - for (LayerList::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { if ((*i)->needsTextLabelHeight()) { sortedLayers[getObjectExportId(*i)] = *i; } @@ -252,17 +262,17 @@ Layer *selectedLayer = 0; - for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if (*i == pc) { selectedLayer = *i; - m_layers.erase(i); + m_layerStack.erase(i); break; } } if (selectedLayer) { m_haveSelectedLayer = true; - m_layers.push_back(selectedLayer); + m_layerStack.push_back(selectedLayer); update(); } else { m_haveSelectedLayer = false; @@ -291,36 +301,36 @@ // subclass might override this } -long +sv_frame_t View::getStartFrame() const { return getFrameForX(0); } -size_t +sv_frame_t View::getEndFrame() const { return getFrameForX(width()) - 1; } void -View::setStartFrame(long f) +View::setStartFrame(sv_frame_t f) { setCentreFrame(f + m_zoomLevel * (width() / 2)); } bool -View::setCentreFrame(size_t f, bool e) +View::setCentreFrame(sv_frame_t f, bool e) { bool changeVisible = false; if (m_centreFrame != f) { - int formerPixel = m_centreFrame / m_zoomLevel; + int formerPixel = int(m_centreFrame / m_zoomLevel); m_centreFrame = f; - int newPixel = m_centreFrame / m_zoomLevel; + int newPixel = int(m_centreFrame / m_zoomLevel); if (newPixel != formerPixel) { @@ -333,8 +343,8 @@ } if (e) { - size_t rf = alignToReference(f); -#ifdef DEBUG_VIEW_WIDGET_PAINT + sv_frame_t rf = alignToReference(f); +#ifdef DEBUG_VIEW cerr << "View[" << this << "]::setCentreFrame(" << f << "): emitting centreFrameChanged(" << rf << ")" << endl; @@ -347,29 +357,31 @@ } int -View::getXForFrame(long frame) const +View::getXForFrame(sv_frame_t frame) const { - return (frame - getStartFrame()) / m_zoomLevel; + return int((frame - getStartFrame()) / m_zoomLevel); } -long +sv_frame_t View::getFrameForX(int x) const { - long z = (long)m_zoomLevel; - long frame = m_centreFrame - (width()/2) * z; + sv_frame_t z = m_zoomLevel; // nb not just int, or multiplication may overflow + sv_frame_t frame = m_centreFrame - (width()/2) * z; + + frame = (frame / z) * z; // this is start frame + frame = frame + x * z; #ifdef DEBUG_VIEW_WIDGET_PAINT - SVDEBUG << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << endl; + cerr << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << endl; #endif - frame = (frame / z) * z; // this is start frame - return frame + x * z; + return frame; } -float -View::getYForFrequency(float frequency, - float minf, - float maxf, +double +View::getYForFrequency(double frequency, + double minf, + double maxf, bool logarithmic) const { Profiler profiler("View::getYForFrequency"); @@ -378,20 +390,20 @@ if (logarithmic) { - static float lastminf = 0.0, lastmaxf = 0.0; - static float logminf = 0.0, logmaxf = 0.0; + static double lastminf = 0.0, lastmaxf = 0.0; + static double logminf = 0.0, logmaxf = 0.0; if (lastminf != minf) { lastminf = (minf == 0.0 ? 1.0 : minf); - logminf = log10f(minf); + logminf = log10(minf); } if (lastmaxf != maxf) { lastmaxf = (maxf < lastminf ? lastminf : maxf); - logmaxf = log10f(maxf); + logmaxf = log10(maxf); } if (logminf == logmaxf) return 0; - return h - (h * (log10f(frequency) - logminf)) / (logmaxf - logminf); + return h - (h * (log10(frequency) - logminf)) / (logmaxf - logminf); } else { @@ -400,30 +412,30 @@ } } -float +double View::getFrequencyForY(int y, - float minf, - float maxf, + double minf, + double maxf, bool logarithmic) const { int h = height(); if (logarithmic) { - static float lastminf = 0.0, lastmaxf = 0.0; - static float logminf = 0.0, logmaxf = 0.0; + static double lastminf = 0.0, lastmaxf = 0.0; + static double logminf = 0.0, logmaxf = 0.0; if (lastminf != minf) { lastminf = (minf == 0.0 ? 1.0 : minf); - logminf = log10f(minf); + logminf = log10(minf); } if (lastmaxf != maxf) { lastmaxf = (maxf < lastminf ? lastminf : maxf); - logmaxf = log10f(maxf); + logmaxf = log10(maxf); } if (logminf == logmaxf) return 0; - return pow(10.f, logminf + ((logmaxf - logminf) * (h - y)) / h); + return pow(10.0, logminf + ((logmaxf - logminf) * (h - y)) / h); } else { @@ -441,9 +453,30 @@ return m_zoomLevel; } +int +View::effectiveDevicePixelRatio() const +{ +#ifdef Q_OS_MAC + int dpratio = devicePixelRatio(); + if (dpratio > 1) { + QSettings settings; + settings.beginGroup("Preferences"); + if (!settings.value("scaledHiDpi", true).toBool()) { + dpratio = 1; + } + settings.endGroup(); + } + return dpratio; +#else + return 1; +#endif +} + void -View::setZoomLevel(size_t z) +View::setZoomLevel(int z) { + int dpratio = effectiveDevicePixelRatio(); + if (z < dpratio) return; if (z < 1) z = 1; if (m_zoomLevel != int(z)) { m_zoomLevel = z; @@ -461,8 +494,8 @@ Layer::ColourSignificance maxSignificance = Layer::ColourAbsent; bool mostSignificantHasDarkBackground = false; - for (LayerList::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { Layer::ColourSignificance s = (*i)->getLayerColourSignificance(); bool light = (*i)->hasLightBackground(); @@ -516,11 +549,6 @@ else return Qt::white; } -View::LayerProgressBar::LayerProgressBar(QWidget *parent) : - QProgressBar(parent) -{ -} - void View::addLayer(Layer *layer) { @@ -530,7 +558,8 @@ SingleColourLayer *scl = dynamic_cast(layer); if (scl) scl->setDefaultColourFor(this); - m_layers.push_back(layer); + m_fixedOrderLayers.push_back(layer); + m_layerStack.push_back(layer); QProgressBar *pb = new QProgressBar(this); pb->setMinimum(0); @@ -538,7 +567,14 @@ pb->setFixedWidth(80); pb->setTextVisible(false); + QPushButton *cancel = new QPushButton(this); + cancel->setIcon(IconLoader().load("fileclose")); + cancel->setFlat(true); + cancel->setFixedSize(QSize(20, 20)); + connect(cancel, SIGNAL(clicked()), this, SLOT(cancelClicked())); + ProgressBarRec pbr; + pbr.cancel = cancel; pbr.bar = pb; pbr.lastCheck = 0; pbr.checkTimer = new QTimer(); @@ -551,6 +587,8 @@ int fs = Preferences::getInstance()->getViewFontSize(); f.setPointSize(std::min(fs, int(ceil(fs * 0.85)))); + cancel->hide(); + pb->setFont(f); pb->hide(); @@ -568,8 +606,8 @@ this, SLOT(modelCompletionChanged())); connect(layer, SIGNAL(modelAlignmentCompletionChanged()), this, SLOT(modelAlignmentCompletionChanged())); - connect(layer, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(modelChanged(size_t, size_t))); + connect(layer, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), + this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); connect(layer, SIGNAL(modelReplaced()), this, SLOT(modelReplaced())); @@ -588,11 +626,23 @@ delete m_cache; m_cache = 0; - for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::iterator i = m_fixedOrderLayers.begin(); + i != m_fixedOrderLayers.end(); + ++i) { if (*i == layer) { - m_layers.erase(i); + m_fixedOrderLayers.erase(i); + break; + } + } + + for (LayerList::iterator i = m_layerStack.begin(); + i != m_layerStack.end(); + ++i) { + if (*i == layer) { + m_layerStack.erase(i); if (m_progressBars.find(layer) != m_progressBars.end()) { delete m_progressBars[layer].bar; + delete m_progressBars[layer].cancel; delete m_progressBars[layer].checkTimer; m_progressBars.erase(layer); } @@ -612,8 +662,8 @@ this, SLOT(modelCompletionChanged())); disconnect(layer, SIGNAL(modelAlignmentCompletionChanged()), this, SLOT(modelAlignmentCompletionChanged())); - disconnect(layer, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(modelChanged(size_t, size_t))); + disconnect(layer, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), + this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t))); disconnect(layer, SIGNAL(modelReplaced()), this, SLOT(modelReplaced())); @@ -623,10 +673,36 @@ } Layer * +View::getInteractionLayer() +{ + Layer *sl = getSelectedLayer(); + if (sl && !(sl->isLayerDormant(this))) { + return sl; + } + if (!m_layerStack.empty()) { + int n = getLayerCount(); + while (n > 0) { + --n; + Layer *layer = getLayer(n); + if (!(layer->isLayerDormant(this))) { + return layer; + } + } + } + return 0; +} + +const Layer * +View::getInteractionLayer() const +{ + return const_cast(const_cast(this)->getInteractionLayer()); +} + +Layer * View::getSelectedLayer() { - if (m_haveSelectedLayer && !m_layers.empty()) { - return getLayer(getLayerCount() - 1); + if (m_haveSelectedLayer && !m_layerStack.empty()) { + return getLayer(getLayerCount() - 1); } else { return 0; } @@ -642,29 +718,29 @@ View::setViewManager(ViewManager *manager) { if (m_manager) { - m_manager->disconnect(this, SLOT(globalCentreFrameChanged(unsigned long))); - m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, unsigned long))); - m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(unsigned long))); - m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); + m_manager->disconnect(this, SLOT(globalCentreFrameChanged(sv_frame_t))); + m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, sv_frame_t))); + m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t))); + m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, int, bool))); m_manager->disconnect(this, SLOT(toolModeChanged())); m_manager->disconnect(this, SLOT(selectionChanged())); m_manager->disconnect(this, SLOT(overlayModeChanged())); m_manager->disconnect(this, SLOT(zoomWheelsEnabledChanged())); - disconnect(m_manager, SLOT(viewCentreFrameChanged(unsigned long, bool, PlaybackFollowMode))); - disconnect(m_manager, SLOT(zoomLevelChanged(unsigned long, bool))); + disconnect(m_manager, SLOT(viewCentreFrameChanged(sv_frame_t, bool, PlaybackFollowMode))); + disconnect(m_manager, SLOT(zoomLevelChanged(int, bool))); } m_manager = manager; - connect(m_manager, SIGNAL(globalCentreFrameChanged(unsigned long)), - this, SLOT(globalCentreFrameChanged(unsigned long))); - connect(m_manager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)), - this, SLOT(viewCentreFrameChanged(View *, unsigned long))); - connect(m_manager, SIGNAL(playbackFrameChanged(unsigned long)), - this, SLOT(viewManagerPlaybackFrameChanged(unsigned long))); - - connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)), - this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool))); + connect(m_manager, SIGNAL(globalCentreFrameChanged(sv_frame_t)), + this, SLOT(globalCentreFrameChanged(sv_frame_t))); + connect(m_manager, SIGNAL(viewCentreFrameChanged(View *, sv_frame_t)), + this, SLOT(viewCentreFrameChanged(View *, sv_frame_t))); + connect(m_manager, SIGNAL(playbackFrameChanged(sv_frame_t)), + this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t))); + + connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, int, bool)), + this, SLOT(viewZoomLevelChanged(View *, int, bool))); connect(m_manager, SIGNAL(toolModeChanged()), this, SLOT(toolModeChanged())); @@ -679,25 +755,30 @@ connect(m_manager, SIGNAL(zoomWheelsEnabledChanged()), this, SLOT(zoomWheelsEnabledChanged())); - connect(this, SIGNAL(centreFrameChanged(unsigned long, bool, + connect(this, SIGNAL(centreFrameChanged(sv_frame_t, bool, PlaybackFollowMode)), - m_manager, SLOT(viewCentreFrameChanged(unsigned long, bool, + m_manager, SLOT(viewCentreFrameChanged(sv_frame_t, bool, PlaybackFollowMode))); - connect(this, SIGNAL(zoomLevelChanged(unsigned long, bool)), - m_manager, SLOT(viewZoomLevelChanged(unsigned long, bool))); - -// setCentreFrame(m_manager->getViewInitialCentreFrame()); - - if (m_followPlay == PlaybackScrollPage) { -// SVDEBUG << "View::setViewManager: setting centre frame to global centre frame: " << m_manager->getGlobalCentreFrame() << endl; + connect(this, SIGNAL(zoomLevelChanged(int, bool)), + m_manager, SLOT(viewZoomLevelChanged(int, bool))); + + switch (m_followPlay) { + + case PlaybackScrollPage: + case PlaybackScrollPageWithCentre: setCentreFrame(m_manager->getGlobalCentreFrame(), false); - } else if (m_followPlay == PlaybackScrollContinuous) { -// SVDEBUG << "View::setViewManager: setting centre frame to playback frame: " << m_manager->getPlaybackFrame() << endl; + break; + + case PlaybackScrollContinuous: setCentreFrame(m_manager->getPlaybackFrame(), false); - } else if (m_followPan) { -// SVDEBUG << "View::setViewManager: (follow pan) setting centre frame to global centre frame: " << m_manager->getGlobalCentreFrame() << endl; - setCentreFrame(m_manager->getGlobalCentreFrame(), false); + break; + + case PlaybackIgnore: + if (m_followPan) { + setCentreFrame(m_manager->getGlobalCentreFrame(), false); + } + break; } if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom()); @@ -708,7 +789,7 @@ } void -View::setViewManager(ViewManager *vm, long initialCentreFrame) +View::setViewManager(ViewManager *vm, sv_frame_t initialCentreFrame) { setViewManager(vm); setCentreFrame(initialCentreFrame, false); @@ -822,18 +903,18 @@ } void -View::modelChanged(size_t startFrame, size_t endFrame) +View::modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame) { QObject *obj = sender(); - long myStartFrame = getStartFrame(); - size_t myEndFrame = getEndFrame(); + sv_frame_t myStartFrame = getStartFrame(); + sv_frame_t myEndFrame = getEndFrame(); #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "View(" << this << ")::modelChanged(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << endl; + cerr << "View(" << this << ")::modelChangedWithin(" << startFrame << "," << endFrame << ") [me " << myStartFrame << "," << myEndFrame << "]" << endl; #endif - if (myStartFrame > 0 && endFrame < size_t(myStartFrame)) { + if (myStartFrame > 0 && endFrame < myStartFrame) { checkProgress(obj); return; } @@ -862,7 +943,7 @@ m_cache = 0; } - if (long(startFrame) < myStartFrame) startFrame = myStartFrame; + if (startFrame < myStartFrame) startFrame = myStartFrame; if (endFrame > myEndFrame) endFrame = myEndFrame; checkProgress(obj); @@ -940,11 +1021,11 @@ } void -View::globalCentreFrameChanged(unsigned long rf) +View::globalCentreFrameChanged(sv_frame_t rf) { if (m_followPan) { - size_t f = alignFromReference(rf); -#ifdef DEBUG_VIEW_WIDGET_PAINT + sv_frame_t f = alignFromReference(rf); +#ifdef DEBUG_VIEW cerr << "View[" << this << "]::globalCentreFrameChanged(" << rf << "): setting centre frame to " << f << endl; #endif @@ -953,30 +1034,42 @@ } void -View::viewCentreFrameChanged(View *, unsigned long ) +View::viewCentreFrameChanged(View *, sv_frame_t ) { // We do nothing with this, but a subclass might } void -View::viewManagerPlaybackFrameChanged(unsigned long f) +View::viewManagerPlaybackFrameChanged(sv_frame_t f) { if (m_manager) { if (sender() != m_manager) return; } +#ifdef DEBUG_VIEW + cerr << "View::viewManagerPlaybackFrameChanged(" << f << ")" << endl; +#endif + f = getAlignedPlaybackFrame(); +#ifdef DEBUG_VIEW + cerr << " -> aligned frame = " << f << endl; +#endif + movePlayPointer(f); } void -View::movePlayPointer(unsigned long newFrame) +View::movePlayPointer(sv_frame_t newFrame) { +#ifdef DEBUG_VIEW + cerr << "View(" << this << ")::movePlayPointer(" << newFrame << ")" << endl; +#endif + if (m_playPointerFrame == newFrame) return; bool visibleChange = (getXForFrame(m_playPointerFrame) != getXForFrame(newFrame)); - size_t oldPlayPointerFrame = m_playPointerFrame; + sv_frame_t oldPlayPointerFrame = m_playPointerFrame; m_playPointerFrame = newFrame; if (!visibleChange) return; @@ -984,6 +1077,12 @@ ((QApplication::mouseButtons() != Qt::NoButton) || (QApplication::keyboardModifiers() & Qt::AltModifier)); + bool pointerInVisibleArea = + long(m_playPointerFrame) >= getStartFrame() && + (m_playPointerFrame < getEndFrame() || + // include old pointer location so we know to refresh when moving out + oldPlayPointerFrame < getEndFrame()); + switch (m_followPlay) { case PlaybackScrollContinuous: @@ -993,59 +1092,79 @@ break; case PlaybackScrollPage: - { - int xold = getXForFrame(oldPlayPointerFrame); - update(xold - 4, 0, 9, height()); - - long w = getEndFrame() - getStartFrame(); - w -= w/5; - long sf = (m_playPointerFrame / w) * w - w/8; - - if (m_manager && - m_manager->isPlaying() && - m_manager->getPlaySelectionMode()) { - MultiSelection::SelectionList selections = m_manager->getSelections(); - if (!selections.empty()) { - size_t selectionStart = selections.begin()->getStartFrame(); - if (sf < long(selectionStart) - w / 10) { - sf = long(selectionStart) - w / 10; - } - } - } + case PlaybackScrollPageWithCentre: + + if (!pointerInVisibleArea && somethingGoingOn) { + + m_followPlayIsDetached = true; + + } else if (!pointerInVisibleArea && m_followPlayIsDetached) { + + // do nothing; we aren't tracking until the pointer comes back in + + } else { + + int xold = getXForFrame(oldPlayPointerFrame); + update(xold - 4, 0, 9, height()); + + sv_frame_t w = getEndFrame() - getStartFrame(); + w -= w/5; + sv_frame_t sf = (m_playPointerFrame / w) * w - w/8; + + if (m_manager && + m_manager->isPlaying() && + m_manager->getPlaySelectionMode()) { + MultiSelection::SelectionList selections = m_manager->getSelections(); + if (!selections.empty()) { + sv_frame_t selectionStart = selections.begin()->getStartFrame(); + if (sf < selectionStart - w / 10) { + sf = selectionStart - w / 10; + } + } + } #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "PlaybackScrollPage: f = " << m_playPointerFrame << ", sf = " << sf << ", start frame " - << getStartFrame() << endl; + cerr << "PlaybackScrollPage: f = " << m_playPointerFrame << ", sf = " << sf << ", start frame " + << getStartFrame() << endl; #endif - // We don't consider scrolling unless the pointer is outside - // the clearly visible range already - - int xnew = getXForFrame(m_playPointerFrame); + // We don't consider scrolling unless the pointer is outside + // the central visible range already + + int xnew = getXForFrame(m_playPointerFrame); #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "xnew = " << xnew << ", width = " << width() << endl; + cerr << "xnew = " << xnew << ", width = " << width() << endl; #endif - if (xnew < width()/8 || xnew > (width()*7)/8) { - if (!somethingGoingOn) { - long offset = getFrameForX(width()/2) - getStartFrame(); - long newCentre = sf + offset; - bool changed = setCentreFrame(newCentre, false); - if (changed) { - xold = getXForFrame(oldPlayPointerFrame); - update(xold - 4, 0, 9, height()); - } - } - } - - update(xnew - 4, 0, 9, height()); - - break; - } + bool shouldScroll = (xnew > (width() * 7) / 8); + + if (!m_followPlayIsDetached && (xnew < width() / 8)) { + shouldScroll = true; + } + + if (xnew > width() / 8) { + m_followPlayIsDetached = false; + } else if (somethingGoingOn) { + m_followPlayIsDetached = true; + } + + if (!somethingGoingOn && shouldScroll) { + sv_frame_t offset = getFrameForX(width()/2) - getStartFrame(); + sv_frame_t newCentre = sf + offset; + bool changed = setCentreFrame(newCentre, false); + if (changed) { + xold = getXForFrame(oldPlayPointerFrame); + update(xold - 4, 0, 9, height()); + } + } + + update(xnew - 4, 0, 9, height()); + } + break; case PlaybackIgnore: - if (long(m_playPointerFrame) >= getStartFrame() && + if (m_playPointerFrame >= getStartFrame() && m_playPointerFrame < getEndFrame()) { update(); } @@ -1054,7 +1173,7 @@ } void -View::viewZoomLevelChanged(View *p, unsigned long z, bool locked) +View::viewZoomLevelChanged(View *p, int z, bool locked) { #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View[" << this << "]: viewZoomLevelChanged(" << p << ", " << z << ", " << locked << ")" << endl; @@ -1075,35 +1194,35 @@ update(); } -size_t +sv_frame_t View::getFirstVisibleFrame() const { - long f0 = getStartFrame(); - size_t f = getModelsStartFrame(); - if (f0 < 0 || f0 < long(f)) return f; + sv_frame_t f0 = getStartFrame(); + sv_frame_t f = getModelsStartFrame(); + if (f0 < 0 || f0 < f) return f; return f0; } -size_t +sv_frame_t View::getLastVisibleFrame() const { - size_t f0 = getEndFrame(); - size_t f = getModelsEndFrame(); + sv_frame_t f0 = getEndFrame(); + sv_frame_t f = getModelsEndFrame(); if (f0 > f) return f; return f0; } -size_t +sv_frame_t View::getModelsStartFrame() const { bool first = true; - size_t startFrame = 0; - - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + sv_frame_t startFrame = 0; + + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if ((*i)->getModel() && (*i)->getModel()->isOK()) { - size_t thisStartFrame = (*i)->getModel()->getStartFrame(); + sv_frame_t thisStartFrame = (*i)->getModel()->getStartFrame(); if (first || thisStartFrame < startFrame) { startFrame = thisStartFrame; @@ -1114,17 +1233,17 @@ return startFrame; } -size_t +sv_frame_t View::getModelsEndFrame() const { bool first = true; - size_t endFrame = 0; - - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + sv_frame_t endFrame = 0; + + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if ((*i)->getModel() && (*i)->getModel()->isOK()) { - size_t thisEndFrame = (*i)->getModel()->getEndFrame(); + sv_frame_t thisEndFrame = (*i)->getModel()->getEndFrame(); if (first || thisEndFrame > endFrame) { endFrame = thisEndFrame; @@ -1137,7 +1256,7 @@ return endFrame; } -int +sv_samplerate_t View::getModelsSampleRate() const { //!!! Just go for the first, for now. If we were supporting @@ -1146,7 +1265,7 @@ //!!! nah, this wants to always return the sr of the main model! - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if ((*i)->getModel() && (*i)->getModel()->isOK()) { return (*i)->getModel()->getSampleRate(); } @@ -1189,8 +1308,8 @@ Model *alignedModel = 0; Model *goodModel = 0; - for (LayerList::const_iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { Layer *layer = *i; @@ -1216,17 +1335,17 @@ else return anyModel; } -size_t -View::alignFromReference(size_t f) const +sv_frame_t +View::alignFromReference(sv_frame_t f) const { - if (!m_manager->getAlignMode()) return f; + if (!m_manager || !m_manager->getAlignMode()) return f; Model *aligningModel = getAligningModel(); if (!aligningModel) return f; return aligningModel->alignFromReference(f); } -size_t -View::alignToReference(size_t f) const +sv_frame_t +View::alignToReference(sv_frame_t f) const { if (!m_manager->getAlignMode()) return f; Model *aligningModel = getAligningModel(); @@ -1234,27 +1353,18 @@ return aligningModel->alignToReference(f); } -int +sv_frame_t View::getAlignedPlaybackFrame() const { - int pf = m_manager->getPlaybackFrame(); + if (!m_manager) return 0; + sv_frame_t pf = m_manager->getPlaybackFrame(); if (!m_manager->getAlignMode()) return pf; Model *aligningModel = getAligningModel(); if (!aligningModel) return pf; -/* - Model *pm = m_manager->getPlaybackModel(); - -// cerr << "View[" << this << "]::getAlignedPlaybackFrame: pf = " << pf; - - if (pm) { - pf = pm->alignToReference(pf); -// cerr << " -> " << pf; - } -*/ - int af = aligningModel->alignFromReference(pf); - -// cerr << ", aligned = " << af << endl; + + sv_frame_t af = aligningModel->alignFromReference(pf); + return af; } @@ -1262,7 +1372,7 @@ View::areLayersScrollable() const { // True iff all views are scrollable - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if (!(*i)->isLayerScrollable(this)) return false; } return true; @@ -1279,7 +1389,7 @@ LayerList scrollables; bool metUnscrollable = false; - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { // SVDEBUG << "View::getScrollableBackLayers: calling isLayerDormant on layer " << *i << endl; // cerr << "(name is " << (*i)->objectName() << ")" // << endl; @@ -1315,7 +1425,7 @@ bool started = false; - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if ((*i)->isLayerDormant(this)) continue; if (!started && (*i)->isLayerScrollable(this)) { continue; @@ -1336,22 +1446,22 @@ return nonScrollables; } -size_t -View::getZoomConstraintBlockSize(size_t blockSize, +int +View::getZoomConstraintBlockSize(int blockSize, ZoomConstraint::RoundingDirection dir) const { - size_t candidate = blockSize; + int candidate = blockSize; bool haveCandidate = false; PowerOfSqrtTwoZoomConstraint defaultZoomConstraint; - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint(); if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint; - size_t thisBlockSize = + int thisBlockSize = zoomConstraint->getNearestBlockSize(blockSize, dir); // Go for the block size that's furthest from the one @@ -1370,7 +1480,7 @@ bool View::areLayerColoursSignificant() const { - for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { if ((*i)->getLayerColourSignificance() == Layer::ColourHasMeaningfulValue) return true; if ((*i)->isLayerOpaque()) break; @@ -1381,8 +1491,8 @@ bool View::hasTopLayerTimeXAxis() const { - LayerList::const_iterator i = m_layers.end(); - if (i == m_layers.begin()) return false; + LayerList::const_iterator i = m_layerStack.end(); + if (i == m_layerStack.begin()) return false; --i; return (*i)->hasTimeXAxis(); } @@ -1408,7 +1518,7 @@ void View::scroll(bool right, bool lots, bool e) { - long delta; + sv_frame_t delta; if (lots) { delta = (getEndFrame() - getStartFrame()) / 2; } else { @@ -1416,9 +1526,9 @@ } if (right) delta = -delta; - if (int(m_centreFrame) < delta) { + if (m_centreFrame < delta) { setCentreFrame(0, e); - } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { + } else if (m_centreFrame - delta >= getModelsEndFrame()) { setCentreFrame(getModelsEndFrame(), e); } else { setCentreFrame(m_centreFrame - delta, e); @@ -1426,6 +1536,25 @@ } void +View::cancelClicked() +{ + QPushButton *cancel = qobject_cast(sender()); + if (!cancel) return; + + for (ProgressMap::iterator i = m_progressBars.begin(); + i != m_progressBars.end(); ++i) { + + if (i->second.cancel == cancel) { + + Layer *layer = i->first; + Model *model = layer->getModel(); + + if (model) model->abandon(); + } + } +} + +void View::checkProgress(void *object) { if (!m_showProgress) return; @@ -1436,6 +1565,7 @@ i != m_progressBars.end(); ++i) { QProgressBar *pb = i->second.bar; + QPushButton *cancel = i->second.cancel; if (i->first == object) { @@ -1465,8 +1595,9 @@ //!!! if (wfm || - (wfm = dynamic_cast - (model->getSourceModel()))) { + (model && + (wfm = dynamic_cast + (model->getSourceModel())))) { completion = wfm->getAlignmentCompletion(); // SVDEBUG << "View::checkProgress: Alignment completion = " << completion << endl; if (completion < 100) { @@ -1481,6 +1612,7 @@ if (completion >= 100) { pb->hide(); + cancel->hide(); timer->stop(); } else { @@ -1493,8 +1625,11 @@ timer->start(); } + cancel->move(0, ph - pb->height()/2 - 10); + cancel->show(); + pb->setValue(completion); - pb->move(0, ph - pb->height()); + pb->move(20, ph - pb->height()); pb->show(); pb->update(); @@ -1544,18 +1679,34 @@ void View::setPaintFont(QPainter &paint) { + int scaleFactor = 1; + int dpratio = effectiveDevicePixelRatio(); + if (dpratio > 1) { + QPaintDevice *dev = paint.device(); + if (dynamic_cast(dev) || dynamic_cast(dev)) { + scaleFactor = dpratio; + } + } + QFont font(paint.font()); - font.setPointSize(Preferences::getInstance()->getViewFontSize()); + font.setPointSize(Preferences::getInstance()->getViewFontSize() + * scaleFactor); paint.setFont(font); } +QRect +View::getPaintRect() const +{ + return rect(); +} + void View::paintEvent(QPaintEvent *e) { // Profiler prof("View::paintEvent", false); -// SVDEBUG << "View::paintEvent: centre frame is " << m_centreFrame << endl; - - if (m_layers.empty()) { +// cerr << "View::paintEvent: centre frame is " << m_centreFrame << endl; + + if (m_layerStack.empty()) { QFrame::paintEvent(e); return; } @@ -1585,6 +1736,8 @@ QRect nonCacheRect(cacheRect); + int dpratio = effectiveDevicePixelRatio(); + // If not all layers are scrollable, but some of the back layers // are, we should store only those in the cache. @@ -1593,29 +1746,37 @@ LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged); bool selectionCacheable = nonScrollables.empty(); bool haveSelections = m_manager && !m_manager->getSelections().empty(); - bool selectionDrawn = false; // If all the non-scrollable layers are non-opaque, then we draw // the selection rectangle behind them and cache it. If any are - // opaque, however, we can't cache. + // opaque, however, or if our device-pixel ratio is not 1 (so we + // need to paint direct to the widget), then we can't cache. // - if (!selectionCacheable) { - selectionCacheable = true; - for (LayerList::const_iterator i = nonScrollables.begin(); - i != nonScrollables.end(); ++i) { - if ((*i)->isLayerOpaque()) { - selectionCacheable = false; - break; - } - } - } - - if (selectionCacheable) { - QPoint localPos; - bool closeToLeft, closeToRight; - if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { - selectionCacheable = false; - } + if (dpratio == 1) { + + if (!selectionCacheable) { + selectionCacheable = true; + for (LayerList::const_iterator i = nonScrollables.begin(); + i != nonScrollables.end(); ++i) { + if ((*i)->isLayerOpaque()) { + selectionCacheable = false; + break; + } + } + } + + if (selectionCacheable) { + QPoint localPos; + bool closeToLeft, closeToRight; + if (shouldIlluminateLocalSelection + (localPos, closeToLeft, closeToRight)) { + selectionCacheable = false; + } + } + + } else { + + selectionCacheable = false; } #ifdef DEBUG_VIEW_WIDGET_PAINT @@ -1633,6 +1794,14 @@ m_selectionCached = false; } + QSize scaledCacheSize(scaledSize(size(), dpratio)); + QRect scaledCacheRect(scaledRect(cacheRect, dpratio)); + + if (!m_buffer || scaledCacheSize != m_buffer->size()) { + delete m_buffer; + m_buffer = new QPixmap(scaledCacheSize); + } + if (!scrollables.empty()) { #ifdef DEBUG_VIEW_WIDGET_PAINT @@ -1642,8 +1811,7 @@ if (!m_cache || m_cacheZoomLevel != m_zoomLevel || - width() != m_cache->width() || - height() != m_cache->height()) { + scaledCacheSize != m_cache->size()) { // cache is not valid @@ -1655,7 +1823,7 @@ #endif } else { delete m_cache; - m_cache = new QPixmap(width(), height()); + m_cache = new QPixmap(scaledCacheSize); #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: recreated cache" << endl; #endif @@ -1665,29 +1833,15 @@ } else if (m_cacheCentreFrame != m_centreFrame) { - long dx = + int dx = getXForFrame(m_cacheCentreFrame) - getXForFrame(m_centreFrame); if (dx > -width() && dx < width()) { -#ifdef PIXMAP_COPY_TO_SELF - // This is not normally defined. Copying a pixmap to - // itself doesn't work properly on Windows, Mac, or - // X11 with the raster backend (it only works when - // moving in one direction and then presumably only by - // accident). It does actually seem to be fine on X11 - // with the native backend, but we prefer not to use - // that anyway - paint.begin(m_cache); - paint.drawPixmap(dx, 0, *m_cache); - paint.end(); -#else static QPixmap *tmpPixmap = 0; - if (!tmpPixmap || - tmpPixmap->width() != width() || - tmpPixmap->height() != height()) { + if (!tmpPixmap || tmpPixmap->size() != scaledCacheSize) { delete tmpPixmap; - tmpPixmap = new QPixmap(width(), height()); + tmpPixmap = new QPixmap(scaledCacheSize); } paint.begin(tmpPixmap); paint.drawPixmap(0, 0, *m_cache); @@ -1695,7 +1849,6 @@ paint.begin(m_cache); paint.drawPixmap(dx, 0, *tmpPixmap); paint.end(); -#endif if (dx < 0) { cacheRect = QRect(width() + dx, 0, -dx, height()); } else { @@ -1716,8 +1869,8 @@ #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: cache is good" << endl; #endif - paint.begin(this); - paint.drawPixmap(cacheRect, *m_cache, cacheRect); + paint.begin(m_buffer); + paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); paint.end(); QFrame::paintEvent(e); paintedCacheRect = true; @@ -1733,16 +1886,26 @@ // Scrollable (cacheable) items first + ViewProxy proxy(this, dpratio); + if (!paintedCacheRect) { - if (repaintCache) paint.begin(m_cache); - else paint.begin(this); + QRect rectToPaint; + + if (repaintCache) { + paint.begin(m_cache); + rectToPaint = scaledCacheRect; + } else { + paint.begin(m_buffer); + rectToPaint = scaledCacheRect; + } + setPaintFont(paint); - paint.setClipRect(cacheRect); + paint.setClipRect(rectToPaint); paint.setPen(getBackground()); paint.setBrush(getBackground()); - paint.drawRect(cacheRect); + paint.drawRect(rectToPaint); paint.setPen(getForeground()); paint.setBrush(Qt::NoBrush); @@ -1750,22 +1913,25 @@ for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { paint.setRenderHint(QPainter::Antialiasing, false); paint.save(); - (*i)->paint(this, paint, cacheRect); +#ifdef DEBUG_VIEW_WIDGET_PAINT + cerr << "Painting scrollable layer " << *i << " using proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << rectToPaint.x() << "," << rectToPaint.y() << " " << rectToPaint.width() << "x" << rectToPaint.height() << endl; +#endif + (*i)->paint(&proxy, paint, rectToPaint); paint.restore(); } if (haveSelections && selectionCacheable) { drawSelections(paint); m_selectionCached = repaintCache; - selectionDrawn = true; } paint.end(); if (repaintCache) { cacheRect |= (e ? e->rect() : rect()); - paint.begin(this); - paint.drawPixmap(cacheRect, *m_cache, cacheRect); + scaledCacheRect = scaledRect(cacheRect, dpratio); + paint.begin(m_buffer); + paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); paint.end(); } } @@ -1776,13 +1942,15 @@ nonCacheRect |= cacheRect; - paint.begin(this); - paint.setClipRect(nonCacheRect); + QRect scaledNonCacheRect = scaledRect(nonCacheRect, dpratio); + + paint.begin(m_buffer); + paint.setClipRect(scaledNonCacheRect); setPaintFont(paint); if (scrollables.empty()) { paint.setPen(getBackground()); paint.setBrush(getBackground()); - paint.drawRect(nonCacheRect); + paint.drawRect(scaledNonCacheRect); } paint.setPen(getForeground()); @@ -1790,10 +1958,18 @@ for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { // Profiler profiler2("View::paintEvent non-cacheable"); - (*i)->paint(this, paint, nonCacheRect); +#ifdef DEBUG_VIEW_WIDGET_PAINT + cerr << "Painting non-scrollable layer " << *i << " without proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << nonCacheRect.x() << "," << nonCacheRect.y() << " " << nonCacheRect.width() << "x" << nonCacheRect.height() << endl; +#endif + (*i)->paint(&proxy, paint, scaledNonCacheRect); } paint.end(); + + paint.begin(this); + QRect finalPaintRect = e ? e->rect() : rect(); + paint.drawPixmap(finalPaintRect, *m_buffer, scaledRect(finalPaintRect, dpratio)); + paint.end(); paint.begin(this); setPaintFont(paint); @@ -1806,16 +1982,19 @@ bool showPlayPointer = true; if (m_followPlay == PlaybackScrollContinuous) { showPlayPointer = false; - } else if (long(m_playPointerFrame) <= getStartFrame() || + } else if (m_playPointerFrame <= getStartFrame() || m_playPointerFrame >= getEndFrame()) { showPlayPointer = false; } else if (m_manager && !m_manager->isPlaying()) { if (m_playPointerFrame == getCentreFrame() && + m_manager->shouldShowCentreLine() && m_followPlay != PlaybackIgnore) { + // Don't show the play pointer when it is redundant with + // the centre line showPlayPointer = false; } } - + if (showPlayPointer) { paint.begin(this); @@ -1864,10 +2043,10 @@ paint.setBrush(Qt::NoBrush); } - int sampleRate = getModelsSampleRate(); + sv_samplerate_t sampleRate = getModelsSampleRate(); QPoint localPos; - long illuminateFrame = -1; + sv_frame_t illuminateFrame = -1; bool closeToLeft, closeToRight; if (shouldIlluminateLocalSelection(localPos, closeToLeft, closeToRight)) { @@ -2035,7 +2214,7 @@ int fontHeight = paint.fontMetrics().height(); int fontAscent = paint.fontMetrics().ascent(); - float v0, v1; + double v0, v1; QString u0, u1; bool b0 = false, b1 = false; @@ -2107,7 +2286,7 @@ } bool bd = false; - float dy = 0.f; + double dy = 0.f; QString du; // dimension, height @@ -2118,7 +2297,7 @@ if (du != "") { if (du == "Hz") { int semis; - float cents; + double cents; semis = Pitch::getPitchForFrequencyDifference(v0, v1, ¢s); dys = QString("[%1 %2 (%3)]") .arg(dy).arg(du) @@ -2223,19 +2402,19 @@ } bool -View::render(QPainter &paint, int xorigin, size_t f0, size_t f1) +View::render(QPainter &paint, int xorigin, sv_frame_t f0, sv_frame_t f1) { - size_t x0 = f0 / m_zoomLevel; - size_t x1 = f1 / m_zoomLevel; - - size_t w = x1 - x0; - - size_t origCentreFrame = m_centreFrame; + int x0 = int(f0 / m_zoomLevel); + int x1 = int(f1 / m_zoomLevel); + + int w = x1 - x0; + + sv_frame_t origCentreFrame = m_centreFrame; bool someLayersIncomplete = false; - for (LayerList::iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { + for (LayerList::iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { int c = (*i)->getCompletion(this); if (c < 100) { @@ -2253,11 +2432,11 @@ while (layerCompletion < 100) { - for (LayerList::iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { + for (LayerList::iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { int c = (*i)->getCompletion(this); - if (i == m_layers.begin() || c < layerCompletion) { + if (i == m_layerStack.begin() || c < layerCompletion) { layerCompletion = c; } } @@ -2278,7 +2457,7 @@ QProgressDialog progress(tr("Rendering image..."), tr("Cancel"), 0, w / width(), this); - for (size_t x = 0; x < w; x += width()) { + for (int x = 0; x < w; x += width()) { progress.setValue(x / width()); qApp->processEvents(); @@ -2300,25 +2479,25 @@ paint.setPen(getForeground()); paint.setBrush(Qt::NoBrush); - for (LayerList::iterator i = m_layers.begin(); - i != m_layers.end(); ++i) { - if(!((*i)->isLayerDormant(this))){ - - paint.setRenderHint(QPainter::Antialiasing, false); - - paint.save(); - paint.translate(xorigin + x, 0); - - cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl; - - (*i)->setSynchronousPainting(true); - - (*i)->paint(this, paint, chunk); - - (*i)->setSynchronousPainting(false); - - paint.restore(); - } + for (LayerList::iterator i = m_layerStack.begin(); + i != m_layerStack.end(); ++i) { + if (!((*i)->isLayerDormant(this))){ + + paint.setRenderHint(QPainter::Antialiasing, false); + + paint.save(); + paint.translate(xorigin + x, 0); + + cerr << "Centre frame now: " << m_centreFrame << " drawing to " << chunk.x() + x + xorigin << ", " << chunk.width() << endl; + + (*i)->setSynchronousPainting(true); + + (*i)->paint(this, paint, chunk); + + (*i)->setSynchronousPainting(false); + + paint.restore(); + } } } @@ -2330,17 +2509,17 @@ QImage * View::toNewImage() { - size_t f0 = getModelsStartFrame(); - size_t f1 = getModelsEndFrame(); + sv_frame_t f0 = getModelsStartFrame(); + sv_frame_t f1 = getModelsEndFrame(); return toNewImage(f0, f1); } QImage * -View::toNewImage(size_t f0, size_t f1) +View::toNewImage(sv_frame_t f0, sv_frame_t f1) { - size_t x0 = f0 / getZoomLevel(); - size_t x1 = f1 / getZoomLevel(); + int x0 = int(f0 / getZoomLevel()); + int x1 = int(f1 / getZoomLevel()); QImage *image = new QImage(x1 - x0, height(), QImage::Format_RGB32); @@ -2358,17 +2537,17 @@ QSize View::getImageSize() { - size_t f0 = getModelsStartFrame(); - size_t f1 = getModelsEndFrame(); + sv_frame_t f0 = getModelsStartFrame(); + sv_frame_t f1 = getModelsEndFrame(); return getImageSize(f0, f1); } QSize -View::getImageSize(size_t f0, size_t f1) +View::getImageSize(sv_frame_t f0, sv_frame_t f1) { - size_t x0 = f0 / getZoomLevel(); - size_t x1 = f1 / getZoomLevel(); + int x0 = int(f0 / getZoomLevel()); + int x1 = int(f1 / getZoomLevel()); return QSize(x1 - x0, height()); } @@ -2391,14 +2570,16 @@ .arg(m_followPan) .arg(m_followZoom) .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" : - m_followPlay == PlaybackScrollPage ? "page" : "ignore") + m_followPlay == PlaybackScrollPageWithCentre ? "page" : + m_followPlay == PlaybackScrollPage ? "daw" : + "ignore") .arg(extraAttributes); - for (size_t i = 0; i < m_layers.size(); ++i) { - bool visible = !m_layers[i]->isLayerDormant(this); - m_layers[i]->toBriefXml(stream, indent + " ", - QString("visible=\"%1\"") - .arg(visible ? "true" : "false")); + for (int i = 0; i < (int)m_fixedOrderLayers.size(); ++i) { + bool visible = !m_fixedOrderLayers[i]->isLayerDormant(this); + m_fixedOrderLayers[i]->toBriefXml(stream, indent + " ", + QString("visible=\"%1\"") + .arg(visible ? "true" : "false")); } stream << indent + "\n"; @@ -2407,7 +2588,11 @@ ViewPropertyContainer::ViewPropertyContainer(View *v) : m_v(v) { +// cerr << "ViewPropertyContainer: " << this << " is owned by View " << v << endl; connect(m_v, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), this, SIGNAL(propertyChanged(PropertyContainer::PropertyName))); } +ViewPropertyContainer::~ViewPropertyContainer() +{ +} diff -r 282f4be8f058 -r c02c51ae5238 view/View.h --- a/view/View.h Tue Jul 14 15:04:46 2015 +0100 +++ b/view/View.h Wed Apr 20 12:06:28 2016 +0100 @@ -19,16 +19,21 @@ #include #include +#include "LayerGeometryProvider.h" + #include "base/ZoomConstraint.h" #include "base/PropertyContainer.h" #include "ViewManager.h" #include "base/XmlExportable.h" +#include "base/BaseTypes.h" // #define DEBUG_VIEW_WIDGET_PAINT 1 class Layer; class ViewPropertyContainer; +class QPushButton; + #include #include @@ -46,7 +51,8 @@ */ class View : public QFrame, - public XmlExportable + public XmlExportable, + public LayerGeometryProvider { Q_OBJECT @@ -58,17 +64,23 @@ virtual ~View(); /** + * Retrieve the id of this object. Views have their own unique + * ids, but ViewProxy objects share the id of their View. + */ + int getId() const { return m_id; } + + /** * Retrieve the first visible sample frame on the widget. * This is a calculated value based on the centre-frame, widget * width and zoom level. The result may be negative. */ - long getStartFrame() const; + sv_frame_t getStartFrame() const; /** * Set the widget pan based on the given first visible frame. The * frame value may be negative. */ - void setStartFrame(long); + void setStartFrame(sv_frame_t); /** * Return the centre frame of the visible widget. This is an @@ -76,30 +88,44 @@ * frame values (start, end) are calculated from this based on the * zoom and other factors. */ - size_t getCentreFrame() const { return m_centreFrame; } + sv_frame_t getCentreFrame() const { return m_centreFrame; } /** * Set the centre frame of the visible widget. */ - void setCentreFrame(size_t f) { setCentreFrame(f, true); } + void setCentreFrame(sv_frame_t f) { setCentreFrame(f, true); } /** * Retrieve the last visible sample frame on the widget. * This is a calculated value based on the centre-frame, widget * width and zoom level. */ - size_t getEndFrame() const; + sv_frame_t getEndFrame() const; /** * Return the pixel x-coordinate corresponding to a given sample * frame (which may be negative). */ - int getXForFrame(long frame) const; + int getXForFrame(sv_frame_t frame) const; /** * Return the closest frame to the given pixel x-coordinate. */ - long getFrameForX(int x) const; + sv_frame_t getFrameForX(int x) const; + + /** + * Return the closest pixel x-coordinate corresponding to a given + * view x-coordinate. Default is no scaling, ViewProxy handles + * scaling case. + */ + int getXForViewX(int viewx) const { return viewx; } + + /** + * Return the closest view x-coordinate corresponding to a given + * pixel x-coordinate. Default is no scaling, ViewProxy handles + * scaling case. + */ + int getViewXForX(int x) const { return x; } /** * Return the pixel y-coordinate corresponding to a given @@ -109,7 +135,7 @@ * * Not thread-safe in logarithmic mode. Call only from GUI thread. */ - float getYForFrequency(float frequency, float minFreq, float maxFreq, + double getYForFrequency(double frequency, double minFreq, double maxFreq, bool logarithmic) const; /** @@ -118,7 +144,7 @@ * * Not thread-safe in logarithmic mode. Call only from GUI thread. */ - float getFrequencyForY(int y, float minFreq, float maxFreq, + double getFrequencyForY(int y, double minFreq, double maxFreq, bool logarithmic) const; /** @@ -131,7 +157,7 @@ * centre frame will be unchanged; the start and end frames will * change. */ - virtual void setZoomLevel(size_t z); + virtual void setZoomLevel(int z); /** * Zoom in or out. @@ -143,39 +169,91 @@ */ virtual void scroll(bool right, bool lots, bool doEmit = true); + /** + * Add a layer to the view. (Normally this should be handled + * through some command abstraction instead of using this function + * directly.) + */ virtual void addLayer(Layer *v); - virtual void removeLayer(Layer *v); // does not delete the layer - virtual int getLayerCount() const { return m_layers.size(); } /** - * Return a layer, counted in stacking order. That is, layer 0 is - * the bottom layer and layer "getLayerCount()-1" is the top one. + * Remove a layer from the view. Does not delete the + * layer. (Normally this should be handled through some command + * abstraction instead of using this function directly.) + */ + virtual void removeLayer(Layer *v); + + /** + * Return the number of layers, regardless of whether visible or + * dormant, i.e. invisible, in this view. + */ + virtual int getLayerCount() const { return int(m_layerStack.size()); } + + /** + * Return the nth layer, counted in stacking order. That is, + * layer 0 is the bottom layer and layer "getLayerCount()-1" is + * the top one. The returned layer may be visible or it may be + * dormant, i.e. invisible. */ virtual Layer *getLayer(int n) { - if (n < int(m_layers.size())) return m_layers[n]; else return 0; + if (in_range_for(m_layerStack, n)) return m_layerStack[n]; + else return 0; } /** - * Return the top layer. This is the same as - * getLayer(getLayerCount()-1) if there is at least one layer, and - * 0 otherwise. + * Return the nth layer, counted in the order they were + * added. Unlike the stacking order used in getLayer(), which + * changes each time a layer is selected, this ordering remains + * fixed. The returned layer may be visible or it may be dormant, + * i.e. invisible. */ - virtual Layer *getTopLayer() { - return m_layers.empty() ? 0 : m_layers[m_layers.size()-1]; + virtual Layer *getFixedOrderLayer(int n) { + if (n < int(m_fixedOrderLayers.size())) return m_fixedOrderLayers[n]; + else return 0; } /** - * Return the layer last selected by the user. This is normally - * the top layer, the same as getLayer(getLayerCount()-1). - * However, if the user has selected the pane itself more recently - * than any of the layers on it, this function will return 0. It - * will also return 0 if there are no layers. + * Return the layer currently active for tool interaction. This is + * the topmost non-dormant (i.e. visible) layer in the view. If + * there are no visible layers in the view, return 0. + */ + virtual Layer *getInteractionLayer(); + + virtual const Layer *getInteractionLayer() const; + + /** + * Return the layer most recently selected by the user. This is + * the layer that any non-tool-driven commands should operate on, + * in the case where this view is the "current" one. + * + * If the user has selected the view itself more recently than any + * of the layers on it, this function will return 0, and any + * non-tool-driven layer commands should be deactivated while this + * view is current. It will also return 0 if there are no layers + * in the view. + * + * Note that, unlike getInteractionLayer(), this could return an + * invisible (dormant) layer. */ virtual Layer *getSelectedLayer(); + virtual const Layer *getSelectedLayer() const; + /** + * Return the "top" layer in the view, whether visible or dormant. + * This is the same as getLayer(getLayerCount()-1) if there is at + * least one layer, and 0 otherwise. + * + * For most purposes involving interaction or commands, you + * probably want either getInteractionLayer() or + * getSelectedLayer() instead. + */ + virtual Layer *getTopLayer() { + return m_layerStack.empty() ? 0 : m_layerStack[m_layerStack.size()-1]; + } + virtual void setViewManager(ViewManager *m); - virtual void setViewManager(ViewManager *m, long initialFrame); + virtual void setViewManager(ViewManager *m, sv_frame_t initialFrame); virtual ViewManager *getViewManager() const { return m_manager; } virtual void setFollowGlobalPan(bool f); @@ -188,18 +266,15 @@ virtual QColor getForeground() const; virtual QColor getBackground() const; - enum TextStyle { - BoxedText, - OutlinedText, - OutlinedItalicText - }; - virtual void drawVisibleText(QPainter &p, int x, int y, QString text, TextStyle style) const; virtual void drawMeasurementRect(QPainter &p, const Layer *, QRect rect, bool focus) const; + virtual bool shouldShowFeatureLabels() const { + return m_manager && m_manager->shouldShowFeatureLabels(); + } virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const { return false; } @@ -229,41 +304,60 @@ } virtual QString getPropertyContainerIconName() const = 0; - virtual size_t getPropertyContainerCount() const; + virtual int getPropertyContainerCount() const; - virtual const PropertyContainer *getPropertyContainer(size_t i) const; - virtual PropertyContainer *getPropertyContainer(size_t i); + // The 0th property container is the view's own; the rest are the + // layers in fixed-order series + virtual const PropertyContainer *getPropertyContainer(int i) const; + virtual PropertyContainer *getPropertyContainer(int i); // Render the contents on a wide canvas - virtual QImage *toNewImage(size_t f0, size_t f1); + virtual QImage *toNewImage(sv_frame_t f0, sv_frame_t f1); virtual QImage *toNewImage(); - virtual QSize getImageSize(size_t f0, size_t f1); + virtual QSize getImageSize(sv_frame_t f0, sv_frame_t f1); virtual QSize getImageSize(); virtual int getTextLabelHeight(const Layer *layer, QPainter &) const; - virtual bool getValueExtents(QString unit, float &min, float &max, + virtual bool getValueExtents(QString unit, double &min, double &max, bool &log) const; virtual void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; // First frame actually in model, to right of scale, if present - virtual size_t getFirstVisibleFrame() const; - virtual size_t getLastVisibleFrame() const; + virtual sv_frame_t getFirstVisibleFrame() const; + virtual sv_frame_t getLastVisibleFrame() const; - size_t getModelsStartFrame() const; - size_t getModelsEndFrame() const; + sv_frame_t getModelsStartFrame() const; + sv_frame_t getModelsEndFrame() const; + + /** + * To be called from a layer, to obtain the extent of the surface + * that the layer is currently painting to. This may be the extent + * of the view (if 1x display scaling is in effect) or of a larger + * cached pixmap (if greater display scaling is in effect). + */ + QRect getPaintRect() const; + + QSize getPaintSize() const { return getPaintRect().size(); } + int getPaintWidth() const { return getPaintRect().width(); } + int getPaintHeight() const { return getPaintRect().height(); } typedef std::set ModelSet; ModelSet getModels(); //!!! Model *getAligningModel() const; - size_t alignFromReference(size_t) const; - size_t alignToReference(size_t) const; - int getAlignedPlaybackFrame() const; + sv_frame_t alignFromReference(sv_frame_t) const; + sv_frame_t alignToReference(sv_frame_t) const; + sv_frame_t getAlignedPlaybackFrame() const; + void updatePaintRect(QRect r) { update(r); } + + View *getView() { return this; } + const View *getView() const { return this; } + signals: void propertyContainerAdded(PropertyContainer *pc); void propertyContainerRemoved(PropertyContainer *pc); @@ -275,17 +369,17 @@ void layerModelChanged(); - void centreFrameChanged(unsigned long frame, + void centreFrameChanged(sv_frame_t frame, bool globalScroll, PlaybackFollowMode followMode); - void zoomLevelChanged(unsigned long, bool); + void zoomLevelChanged(int, bool); void contextHelpChanged(const QString &); public slots: virtual void modelChanged(); - virtual void modelChanged(size_t startFrame, size_t endFrame); + virtual void modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame); virtual void modelCompletionChanged(); virtual void modelAlignmentCompletionChanged(); virtual void modelReplaced(); @@ -294,10 +388,10 @@ virtual void layerMeasurementRectsChanged(); virtual void layerNameChanged(); - virtual void globalCentreFrameChanged(unsigned long); - virtual void viewCentreFrameChanged(View *, unsigned long); - virtual void viewManagerPlaybackFrameChanged(unsigned long); - virtual void viewZoomLevelChanged(View *, unsigned long, bool); + virtual void globalCentreFrameChanged(sv_frame_t); + virtual void viewCentreFrameChanged(View *, sv_frame_t); + virtual void viewManagerPlaybackFrameChanged(sv_frame_t); + virtual void viewZoomLevelChanged(View *, int, bool); virtual void propertyContainerSelected(View *, PropertyContainer *pc); @@ -306,23 +400,36 @@ virtual void overlayModeChanged(); virtual void zoomWheelsEnabledChanged(); + virtual void cancelClicked(); + virtual void progressCheckStalledTimerElapsed(); protected: View(QWidget *, bool showProgress); + + int m_id; + virtual void paintEvent(QPaintEvent *e); virtual void drawSelections(QPainter &); virtual bool shouldLabelSelections() const { return true; } - virtual bool render(QPainter &paint, int x0, size_t f0, size_t f1); + virtual bool render(QPainter &paint, int x0, sv_frame_t f0, sv_frame_t f1); virtual void setPaintFont(QPainter &paint); + + QSize scaledSize(const QSize &s, int factor) { + return QSize(s.width() * factor, s.height() * factor); + } + QRect scaledRect(const QRect &r, int factor) { + return QRect(r.x() * factor, r.y() * factor, + r.width() * factor, r.height() * factor); + } typedef std::vector LayerList; - int getModelsSampleRate() const; + sv_samplerate_t getModelsSampleRate() const; bool areLayersScrollable() const; LayerList getScrollableBackLayers(bool testChanged, bool &changed) const; LayerList getNonScrollableFrontLayers(bool testChanged, bool &changed) const; - size_t getZoomConstraintBlockSize(size_t blockSize, + int getZoomConstraintBlockSize(int blockSize, ZoomConstraint::RoundingDirection dir = ZoomConstraint::RoundNearest) const; @@ -337,30 +444,35 @@ // false. bool hasTopLayerTimeXAxis() const; - bool setCentreFrame(size_t f, bool doEmit); + bool setCentreFrame(sv_frame_t f, bool doEmit); - void movePlayPointer(unsigned long f); + void movePlayPointer(sv_frame_t f); void checkProgress(void *object); int getProgressBarWidth() const; // if visible - size_t m_centreFrame; + int effectiveDevicePixelRatio() const; + + sv_frame_t m_centreFrame; int m_zoomLevel; bool m_followPan; bool m_followZoom; PlaybackFollowMode m_followPlay; - size_t m_playPointerFrame; + bool m_followPlayIsDetached; + sv_frame_t m_playPointerFrame; bool m_lightBackground; bool m_showProgress; QPixmap *m_cache; - size_t m_cacheCentreFrame; + QPixmap *m_buffer; + sv_frame_t m_cacheCentreFrame; int m_cacheZoomLevel; bool m_selectionCached; bool m_deleting; - LayerList m_layers; // I don't own these, but see dtor note above + LayerList m_layerStack; // I don't own these, but see dtor note above + LayerList m_fixedOrderLayers; bool m_haveSelectedLayer; QString m_lastError; @@ -369,16 +481,8 @@ mutable LayerList m_lastScrollableBackLayers; mutable LayerList m_lastNonScrollableBackLayers; - class LayerProgressBar : public QProgressBar { - public: - LayerProgressBar(QWidget *parent); - virtual QString text() const { return m_text; } - virtual void setText(QString text) { m_text = text; } - protected: - QString m_text; - }; - struct ProgressBarRec { + QPushButton *cancel; QProgressBar *bar; int lastCheck; QTimer *checkTimer; @@ -401,6 +505,8 @@ public: ViewPropertyContainer(View *v); + virtual ~ViewPropertyContainer(); + PropertyList getProperties() const { return m_v->getProperties(); } QString getPropertyLabel(const PropertyName &n) const { return m_v->getPropertyLabel(n); diff -r 282f4be8f058 -r c02c51ae5238 view/ViewManager.cpp --- a/view/ViewManager.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/view/ViewManager.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -43,11 +43,12 @@ m_playSelectionMode(false), m_playSoloMode(false), m_alignMode(false), - m_overlayMode(MinimalOverlays), + m_overlayMode(StandardOverlays), m_zoomWheelsEnabled(true), m_showCentreLine(true), m_illuminateLocalFeatures(true), m_showWorkTitle(false), + m_showDuration(true), m_lightPalette(QApplication::palette()), m_darkPalette(QApplication::palette()) { @@ -57,9 +58,9 @@ (settings.value("overlay-mode", int(m_overlayMode)).toInt()); if (m_overlayMode != NoOverlays && - m_overlayMode != MinimalOverlays && + m_overlayMode != StandardOverlays && m_overlayMode != AllOverlays) { - m_overlayMode = MinimalOverlays; + m_overlayMode = StandardOverlays; } m_zoomWheelsEnabled = @@ -125,7 +126,7 @@ { } -unsigned long +sv_frame_t ViewManager::getGlobalCentreFrame() const { #ifdef DEBUG_VIEW_MANAGER @@ -135,7 +136,7 @@ } void -ViewManager::setGlobalCentreFrame(unsigned long f) +ViewManager::setGlobalCentreFrame(sv_frame_t f) { #ifdef DEBUG_VIEW_MANAGER cerr << "ViewManager::setGlobalCentreFrame to " << f << endl; @@ -144,7 +145,7 @@ emit globalCentreFrameChanged(f); } -unsigned long +int ViewManager::getGlobalZoom() const { #ifdef DEBUG_VIEW_MANAGER @@ -153,7 +154,7 @@ return m_globalZoom; } -unsigned long +sv_frame_t ViewManager::getPlaybackFrame() const { if (m_playSource && m_playSource->isPlaying()) { @@ -163,8 +164,9 @@ } void -ViewManager::setPlaybackFrame(unsigned long f) +ViewManager::setPlaybackFrame(sv_frame_t f) { + if (f < 0) f = 0; if (m_playbackFrame != f) { m_playbackFrame = f; emit playbackFrameChanged(f); @@ -186,18 +188,38 @@ m_playbackModel = model; } -size_t -ViewManager::alignPlaybackFrameToReference(size_t frame) const +sv_frame_t +ViewManager::alignPlaybackFrameToReference(sv_frame_t frame) const { - if (!m_playbackModel) return frame; - else return m_playbackModel->alignToReference(frame); +#ifdef DEBUG_VIEW_MANAGER + cerr << "ViewManager::alignPlaybackFrameToReference(" << frame << "): playback model is " << m_playbackModel << endl; +#endif + if (!m_playbackModel) { + return frame; + } else { + sv_frame_t f = m_playbackModel->alignToReference(frame); +#ifdef DEBUG_VIEW_MANAGER + cerr << "aligned frame = " << f << endl; +#endif + return f; + } } -size_t -ViewManager::alignReferenceToPlaybackFrame(size_t frame) const +sv_frame_t +ViewManager::alignReferenceToPlaybackFrame(sv_frame_t frame) const { - if (!m_playbackModel) return frame; - else return m_playbackModel->alignFromReference(frame); +#ifdef DEBUG_VIEW_MANAGER + cerr << "ViewManager::alignReferenceToPlaybackFrame(" << frame << "): playback model is " << m_playbackModel << endl; +#endif + if (!m_playbackModel) { + return frame; + } else { + sv_frame_t f = m_playbackModel->alignFromReference(frame); +#ifdef DEBUG_VIEW_MANAGER + cerr << "aligned frame = " << f << endl; +#endif + return f; + } } bool @@ -258,6 +280,14 @@ } void +ViewManager::addSelectionQuietly(const Selection &selection) +{ + MultiSelection ms(m_selections); + ms.addSelection(selection); + setSelections(ms, true); +} + +void ViewManager::removeSelection(const Selection &selection) { MultiSelection ms(m_selections); @@ -274,15 +304,18 @@ } void -ViewManager::setSelections(const MultiSelection &ms) +ViewManager::setSelections(const MultiSelection &ms, bool quietly) { if (m_selections.getSelections() == ms.getSelections()) return; SetSelectionCommand *command = new SetSelectionCommand(this, ms); CommandHistory::getInstance()->addCommand(command); + if (!quietly) { + emit selectionChangedByUser(); + } } -size_t -ViewManager::constrainFrameToSelection(size_t frame) const +sv_frame_t +ViewManager::constrainFrameToSelection(sv_frame_t frame) const { MultiSelection::SelectionList sl = getSelections(); if (sl.empty()) return frame; @@ -341,7 +374,7 @@ } Selection -ViewManager::getContainingSelection(size_t frame, bool defaultToFollowing) const +ViewManager::getContainingSelection(sv_frame_t frame, bool defaultToFollowing) const { return m_selections.getContainingSelection(frame, defaultToFollowing); } @@ -360,9 +393,32 @@ case DrawMode: emit activity(tr("Enter Draw mode")); break; case EraseMode: emit activity(tr("Enter Erase mode")); break; case MeasureMode: emit activity(tr("Enter Measure mode")); break; + case NoteEditMode: emit activity(tr("Enter NoteEdit mode")); break; // GF: NoteEditMode activity (I'm not yet certain why we need to emit this.) }; } +ViewManager::ToolMode +ViewManager::getToolModeFor(const View *v) const +{ + if (m_toolModeOverrides.find(v) == m_toolModeOverrides.end()) { + return getToolMode(); + } else { + return m_toolModeOverrides.find(v)->second; + } +} + +void +ViewManager::setToolModeFor(const View *v, ToolMode mode) +{ + m_toolModeOverrides[v] = mode; +} + +void +ViewManager::clearToolModeOverrides() +{ + m_toolModeOverrides.clear(); +} + void ViewManager::setPlayLoopMode(bool mode) { @@ -423,7 +479,7 @@ } } -size_t +sv_samplerate_t ViewManager::getPlaybackSampleRate() const { if (m_playSource) { @@ -432,7 +488,7 @@ return 0; } -size_t +sv_samplerate_t ViewManager::getOutputSampleRate() const { if (m_playSource) { @@ -504,7 +560,7 @@ } void -ViewManager::viewCentreFrameChanged(unsigned long f, bool locked, +ViewManager::viewCentreFrameChanged(sv_frame_t f, bool locked, PlaybackFollowMode mode) { View *v = dynamic_cast(sender()); @@ -528,23 +584,22 @@ } } - if (mode == PlaybackIgnore) { - return; + if (mode == PlaybackScrollPageWithCentre || + mode == PlaybackScrollContinuous) { + seek(f); } - - seek(f); } void -ViewManager::seek(unsigned long f) +ViewManager::seek(sv_frame_t f) { #ifdef DEBUG_VIEW_MANAGER cerr << "ViewManager::seek(" << f << ")" << endl; #endif if (m_playSource && m_playSource->isPlaying()) { - unsigned long playFrame = m_playSource->getCurrentPlayingFrame(); - unsigned long diff = std::max(f, playFrame) - std::min(f, playFrame); + sv_frame_t playFrame = m_playSource->getCurrentPlayingFrame(); + sv_frame_t diff = std::max(f, playFrame) - std::min(f, playFrame); if (diff > 20000) { m_playbackFrame = f; m_playSource->play(f); @@ -562,7 +617,7 @@ } void -ViewManager::viewZoomLevelChanged(unsigned long z, bool locked) +ViewManager::viewZoomLevelChanged(int z, bool locked) { View *v = dynamic_cast(sender()); @@ -646,11 +701,13 @@ m_lightPalette = QApplication::palette(); } +#ifndef Q_OS_MAC if (dark) { QApplication::setPalette(m_darkPalette); } else { QApplication::setPalette(m_lightPalette); } +#endif } bool @@ -664,3 +721,23 @@ return dark; } +int +ViewManager::scalePixelSize(int pixels) +{ + static double ratio = 0.0; + if (ratio == 0.0) { + double baseEm; +#ifdef Q_OS_MAC + baseEm = 17.0; +#else + baseEm = 15.0; +#endif + double em = QFontMetrics(QFont()).height(); + ratio = em / baseEm; + } + + int scaled = int(pixels * ratio + 0.5); +// cerr << "scaledSize: " << pixels << " -> " << scaled << " at ratio " << ratio << endl; + if (pixels != 0 && scaled == 0) scaled = 1; + return scaled; +} diff -r 282f4be8f058 -r c02c51ae5238 view/ViewManager.h --- a/view/ViewManager.h Tue Jul 14 15:04:46 2015 +0100 +++ b/view/ViewManager.h Wed Apr 20 12:06:28 2016 +0100 @@ -26,13 +26,37 @@ #include "base/Selection.h" #include "base/Command.h" #include "base/Clipboard.h" +#include "base/BaseTypes.h" class AudioPlaySource; class Model; enum PlaybackFollowMode { + + /** + * View scrolls continuously during playback, keeping the playback + * position at the centre. + */ PlaybackScrollContinuous, + + /** + * View follows playback page-by-page, but dragging the view + * relocates playback to the centre frame. This is the classic + * Sonic Visualiser behaviour. + */ + PlaybackScrollPageWithCentre, + + /** + * View follows playback page-by-page, and the play head is moved + * (by the user) separately from dragging the view. This is + * roughly the behaviour of a typical DAW or audio editor. + */ PlaybackScrollPage, + + /** + * View is detached from playback. It doesn't follow playback, and + * dragging the view does not affect the play head. + */ PlaybackIgnore }; @@ -59,17 +83,17 @@ bool isPlaying() const; - unsigned long getGlobalCentreFrame() const; // the set method is a slot - unsigned long getGlobalZoom() const; + sv_frame_t getGlobalCentreFrame() const; // the set method is a slot + int getGlobalZoom() const; - unsigned long getPlaybackFrame() const; // the set method is a slot + sv_frame_t getPlaybackFrame() const; // the set method is a slot // Only meaningful in solo mode, and used for optional alignment feature Model *getPlaybackModel() const; void setPlaybackModel(Model *); - size_t alignPlaybackFrameToReference(size_t) const; - size_t alignReferenceToPlaybackFrame(size_t) const; + sv_frame_t alignPlaybackFrameToReference(sv_frame_t) const; + sv_frame_t alignReferenceToPlaybackFrame(sv_frame_t) const; bool haveInProgressSelection() const; const Selection &getInProgressSelection(bool &exclusive) const; @@ -83,7 +107,14 @@ void addSelection(const Selection &selection); void removeSelection(const Selection &selection); void clearSelections(); - size_t constrainFrameToSelection(size_t frame) const; + sv_frame_t constrainFrameToSelection(sv_frame_t frame) const; + + /** + * Adding a selection normally emits the selectionChangedByUser + * signal. Call this to add a selection without emitting that signal. + * This is used in session file load, for example. + */ + void addSelectionQuietly(const Selection &selection); /** * Return the selection that contains a given frame. @@ -91,7 +122,7 @@ * selected area, return the next selection after the given frame. * Return the empty selection if no appropriate selection is found. */ - Selection getContainingSelection(size_t frame, bool defaultToFollowing) const; + Selection getContainingSelection(sv_frame_t frame, bool defaultToFollowing) const; Clipboard &getClipboard() { return m_clipboard; } @@ -101,11 +132,19 @@ EditMode, DrawMode, EraseMode, - MeasureMode + MeasureMode, + NoteEditMode //GF: Tonioni: this tool mode will be context sensitive. }; ToolMode getToolMode() const { return m_toolMode; } void setToolMode(ToolMode mode); + /// Override the tool mode for a specific view + void setToolModeFor(const View *v, ToolMode mode); + /// Return override mode if it exists for this view or global mode otherwise + ToolMode getToolModeFor(const View *v) const; + /// Clear all current view-specific overrides + void clearToolModeOverrides(); + bool getPlayLoopMode() const { return m_playLoopMode; } void setPlayLoopMode(bool on); @@ -120,6 +159,7 @@ void setIlluminateLocalFeatures(bool i) { m_illuminateLocalFeatures = i; } void setShowWorkTitle(bool show) { m_showWorkTitle = show; } + void setShowDuration(bool show) { m_showDuration = show; } /** * The sample rate that is used for playback. This is usually the @@ -127,27 +167,35 @@ * differ from this will play back at the wrong speed -- there is * no per-model resampler. */ - size_t getPlaybackSampleRate() const; + sv_samplerate_t getPlaybackSampleRate() const; /** * The sample rate of the audio output device. If the playback * sample rate differs from this, everything will be resampled at * the output stage. */ - size_t getOutputSampleRate() const; + sv_samplerate_t getOutputSampleRate() const; /** * The sample rate of the current main model. This may in theory * differ from the playback sample rate, in which case even the * main model will play at the wrong speed. */ - size_t getMainModelSampleRate() const { return m_mainModelSampleRate; } + sv_samplerate_t getMainModelSampleRate() const { return m_mainModelSampleRate; } - void setMainModelSampleRate(size_t sr) { m_mainModelSampleRate = sr; } + void setMainModelSampleRate(sv_samplerate_t sr) { m_mainModelSampleRate = sr; } + /** + * Take a "design pixel" size and scale it for the actual + * display. This is relevant to hi-dpi systems that do not do + * pixel doubling (i.e. Windows and Linux rather than OS/X). + */ + int scalePixelSize(int pixels); + enum OverlayMode { NoOverlays, - MinimalOverlays, + GlobalOverlays, + StandardOverlays, AllOverlays }; void setOverlayMode(OverlayMode mode); @@ -157,7 +205,7 @@ bool shouldShowCentreLine() const { return m_showCentreLine; } bool shouldShowDuration() const { - return m_overlayMode != NoOverlays; + return m_overlayMode != NoOverlays && m_showDuration; } bool shouldShowFrameCount() const { return m_showCentreLine && shouldShowDuration(); @@ -169,7 +217,7 @@ return m_overlayMode == AllOverlays; } bool shouldShowSelectionExtents() const { - return m_overlayMode != NoOverlays; + return m_overlayMode != NoOverlays && m_overlayMode != GlobalOverlays; } bool shouldShowLayerNames() const { return m_overlayMode == AllOverlays; @@ -183,6 +231,9 @@ bool shouldIlluminateLocalFeatures() const { return m_illuminateLocalFeatures; } + bool shouldShowFeatureLabels() const { + return m_overlayMode != NoOverlays && m_overlayMode != GlobalOverlays; + } void setZoomWheelsEnabled(bool enable); bool getZoomWheelsEnabled() const { return m_zoomWheelsEnabled; } @@ -192,23 +243,28 @@ signals: /** Emitted when user causes the global centre frame to change. */ - void globalCentreFrameChanged(unsigned long frame); + void globalCentreFrameChanged(sv_frame_t frame); /** Emitted when user scrolls a view, but doesn't affect global centre. */ - void viewCentreFrameChanged(View *v, unsigned long frame); + void viewCentreFrameChanged(View *v, sv_frame_t frame); /** Emitted when a view zooms. */ - void viewZoomLevelChanged(View *v, unsigned long zoom, bool locked); + void viewZoomLevelChanged(View *v, int zoom, bool locked); /** Emitted when the playback frame changes. */ - void playbackFrameChanged(unsigned long frame); + void playbackFrameChanged(sv_frame_t frame); /** Emitted when the output levels change. Values in range 0.0 -> 1.0. */ void outputLevelsChanged(float left, float right); - /** Emitted when the selection has changed. */ + /** Emitted whenever the selection has changed. */ void selectionChanged(); + /** Emitted when the selection has been changed through an + * explicit selection-editing action. *Not* emitted when the + * selection has been changed through undo or redo. */ + void selectionChangedByUser(); + /** Emitted when the in-progress (rubberbanding) selection has changed. */ void inProgressSelectionChanged(); @@ -244,24 +300,24 @@ void activity(QString); public slots: - void viewCentreFrameChanged(unsigned long, bool, PlaybackFollowMode); - void viewZoomLevelChanged(unsigned long, bool); - void setGlobalCentreFrame(unsigned long); - void setPlaybackFrame(unsigned long); + void viewCentreFrameChanged(sv_frame_t, bool, PlaybackFollowMode); + void viewZoomLevelChanged(int, bool); + void setGlobalCentreFrame(sv_frame_t); + void setPlaybackFrame(sv_frame_t); void playStatusChanged(bool playing); protected slots: void checkPlayStatus(); - void seek(unsigned long); -//!!! void considerZoomChange(void *, unsigned long, bool); + void seek(sv_frame_t); +//!!! void considerZoomChange(void *, int, bool); protected: AudioPlaySource *m_playSource; - unsigned long m_globalCentreFrame; - unsigned long m_globalZoom; - mutable unsigned long m_playbackFrame; + sv_frame_t m_globalCentreFrame; + int m_globalZoom; + mutable sv_frame_t m_playbackFrame; Model *m_playbackModel; //!!! - size_t m_mainModelSampleRate; + sv_samplerate_t m_mainModelSampleRate; float m_lastLeft; float m_lastRight; @@ -273,13 +329,14 @@ Clipboard m_clipboard; ToolMode m_toolMode; + std::map m_toolModeOverrides; bool m_playLoopMode; bool m_playSelectionMode; bool m_playSoloMode; bool m_alignMode; - void setSelections(const MultiSelection &ms); + void setSelections(const MultiSelection &ms, bool quietly = false); void signalSelectionChange(); class SetSelectionCommand : public Command @@ -302,6 +359,7 @@ bool m_showCentreLine; bool m_illuminateLocalFeatures; bool m_showWorkTitle; + bool m_showDuration; QPalette m_lightPalette; QPalette m_darkPalette; diff -r 282f4be8f058 -r c02c51ae5238 view/ViewProxy.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/view/ViewProxy.h Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,156 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef VIEW_PROXY_H +#define VIEW_PROXY_H + +#include "LayerGeometryProvider.h" + +class ViewProxy : public LayerGeometryProvider +{ +public: + ViewProxy(View *view, int scaleFactor) : + m_view(view), m_scaleFactor(scaleFactor) { } + + virtual int getId() const { + return m_view->getId(); + } + virtual sv_frame_t getStartFrame() const { + return m_view->getStartFrame(); + } + virtual sv_frame_t getCentreFrame() const { + return m_view->getCentreFrame(); + } + virtual sv_frame_t getEndFrame() const { + return m_view->getEndFrame(); + } + virtual int getXForFrame(sv_frame_t frame) const { + //!!! not actually correct, if frame lies between view's pixels + return m_scaleFactor * m_view->getXForFrame(frame); + } + virtual sv_frame_t getFrameForX(int x) const { + sv_frame_t f0 = m_view->getFrameForX(x / m_scaleFactor); + if (m_scaleFactor == 1) return f0; + sv_frame_t f1 = m_view->getFrameForX((x / m_scaleFactor) + 1); + return f0 + ((f1 - f0) * (x % m_scaleFactor)) / m_scaleFactor; + } + virtual int getXForViewX(int viewx) const { + return viewx * m_scaleFactor; + } + virtual int getViewXForX(int x) const { + return x / m_scaleFactor; + } + virtual sv_frame_t getModelsStartFrame() const { + return m_view->getModelsStartFrame(); + } + virtual sv_frame_t getModelsEndFrame() const { + return m_view->getModelsEndFrame(); + } + virtual double getYForFrequency(double frequency, + double minFreq, double maxFreq, + bool logarithmic) const { + return m_scaleFactor * + m_view->getYForFrequency(frequency, minFreq, maxFreq, logarithmic); + } + virtual double getFrequencyForY(int y, double minFreq, double maxFreq, + bool logarithmic) const { + double f0 = m_view->getFrequencyForY + (y / m_scaleFactor, minFreq, maxFreq, logarithmic); + if (m_scaleFactor == 1) return f0; + double f1 = m_view->getFrequencyForY + ((y / m_scaleFactor) + 1, minFreq, maxFreq, logarithmic); + return f0 + ((f1 - f0) * (y % m_scaleFactor)) / m_scaleFactor; + } + virtual int getTextLabelHeight(const Layer *layer, QPainter &paint) const { + return m_scaleFactor * m_view->getTextLabelHeight(layer, paint); + } + virtual bool getValueExtents(QString unit, double &min, double &max, + bool &log) const { + return m_view->getValueExtents(unit, min, max, log); + } + virtual int getZoomLevel() const { + int z = m_view->getZoomLevel(); +// cerr << "getZoomLevel: from " << z << " to "; + z = z / m_scaleFactor; +// cerr << z << endl; + if (z < 1) z = 1; + return z; + } + virtual QRect getPaintRect() const { + QRect r = m_view->getPaintRect(); + return QRect(r.x() * m_scaleFactor, + r.y() * m_scaleFactor, + r.width() * m_scaleFactor, + r.height() * m_scaleFactor); + } + virtual QSize getPaintSize() const { + return getPaintRect().size(); + } + virtual int getPaintWidth() const { + return getPaintRect().width(); + } + virtual int getPaintHeight() const { + return getPaintRect().height(); + } + virtual bool hasLightBackground() const { + return m_view->hasLightBackground(); + } + virtual QColor getForeground() const { + return m_view->getForeground(); + } + virtual QColor getBackground() const { + return m_view->getBackground(); + } + virtual ViewManager *getViewManager() const { + return m_view->getViewManager(); + } + + virtual bool shouldIlluminateLocalFeatures(const Layer *layer, + QPoint &point) const { + QPoint p; + bool should = m_view->shouldIlluminateLocalFeatures(layer, p); + point = QPoint(p.x() * m_scaleFactor, p.y() * m_scaleFactor); + return should; + } + + virtual bool shouldShowFeatureLabels() const { + return m_view->shouldShowFeatureLabels(); + } + + virtual void drawVisibleText(QPainter &p, int x, int y, + QString text, TextStyle style) const { + m_view->drawVisibleText(p, x, y, text, style); + } + + virtual void drawMeasurementRect(QPainter &p, const Layer *layer, + QRect rect, bool focus) const { + m_view->drawMeasurementRect(p, layer, rect, focus); + } + + virtual void updatePaintRect(QRect r) { + m_view->update(r.x() / m_scaleFactor, + r.y() / m_scaleFactor, + r.width() / m_scaleFactor, + r.height() / m_scaleFactor); + } + + virtual View *getView() { return m_view; } + virtual const View *getView() const { return m_view; } + +private: + View *m_view; + int m_scaleFactor; +}; + +#endif diff -r 282f4be8f058 -r c02c51ae5238 view/view.pro --- a/view/view.pro Tue Jul 14 15:04:46 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -TEMPLATE = lib - -SV_UNIT_PACKAGES = -load(../prf/sv.prf) - -CONFIG += sv staticlib qt thread warn_on stl rtti exceptions -QT += xml - -TARGET = svview - -DEPENDPATH += . .. -INCLUDEPATH += . .. -OBJECTS_DIR = tmp_obj -MOC_DIR = tmp_moc - -# Input -HEADERS += Overview.h \ - Pane.h \ - PaneStack.h \ - View.h \ - ViewManager.h -SOURCES += Overview.cpp \ - Pane.cpp \ - PaneStack.cpp \ - View.cpp \ - ViewManager.cpp diff -r 282f4be8f058 -r c02c51ae5238 widgets/ActivityLog.cpp --- a/widgets/ActivityLog.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/ActivityLog.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -25,6 +25,13 @@ #include +#include "base/Debug.h" + +using std::cerr; +using std::endl; + +//#define PRINT_ACTIVITY 1 + ActivityLog::ActivityLog() : QDialog() { setWindowTitle(tr("Activity Log")); @@ -53,9 +60,16 @@ ActivityLog::activityHappened(QString name) { name = name.replace("&", ""); -// SVDEBUG << "ActivityLog::activityHappened(" << name << ")" << endl; + +#ifdef PRINT_ACTIVITY + cerr << "ActivityLog: " << name; if (name == m_prevName) { -// cerr << "(ignoring duplicate)" << endl; + cerr << " (duplicate)"; + } + cerr << endl; +#endif + + if (name == m_prevName) { return; } m_prevName = name; diff -r 282f4be8f058 -r c02c51ae5238 widgets/AudioDial.cpp --- a/widgets/AudioDial.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/AudioDial.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -121,10 +121,10 @@ QPainter paint; - float angle = AUDIO_DIAL_MIN // offset + double angle = AUDIO_DIAL_MIN // offset + (AUDIO_DIAL_RANGE * - (float(QDial::value() - QDial::minimum()) / - (float(QDial::maximum() - QDial::minimum())))); + (double(QDial::value() - QDial::minimum()) / + (double(QDial::maximum() - QDial::minimum())))); int degrees = int(angle * 180.0 / M_PI); int ns = notchSize(); @@ -231,31 +231,31 @@ c = c.light(110); } - // Scale shadow... + // Scale shadow, omitting the bottom part... shadowAngle = 2160; - c = palette().dark().color(); - for (int arc = 120; arc < 2880; arc += 240) { + c = palette().shadow().color(); + for (int i = 0; i < 5; ++i) { pen.setColor(c); paint.setPen(pen); + int arc = i * 240 + 120; paint.drawArc(scale/2, scale/2, width-scale, width-scale, shadowAngle + arc, 240); + c = c.light(110); + } + c = palette().shadow().color(); + for (int i = 0; i < 12; ++i) { + pen.setColor(c); + paint.setPen(pen); + int arc = i * 240 + 120; paint.drawArc(scale/2, scale/2, width-scale, width-scale, shadowAngle - arc, 240); - c = c.light(108); + c = c.light(110); } - // Undraw the bottom part... - - pen.setColor(palette().background().color()); - pen.setWidth(scale * 4); - paint.setPen(pen); - paint.drawArc(scale/2, scale/2, - width-scale, width-scale, -45 * 16, -92 * 16); - // Scale ends... - pen.setColor(palette().dark().color()); + pen.setColor(palette().shadow().color()); pen.setWidth(scale); paint.setPen(pen); for (int i = 0; i < numTicks; ++i) { @@ -268,15 +268,15 @@ // Pointer notch... - float hyp = float(width) / 2.0; - float len = hyp - indent; + double hyp = double(width) / 2.0; + double len = hyp - indent; --len; - float x0 = hyp; - float y0 = hyp; + double x0 = hyp; + double y0 = hyp; - float x = hyp - len * sin(angle); - float y = hyp + len * cos(angle); + double x = hyp - len * sin(angle); + double y = hyp + len * cos(angle); c = palette().dark().color(); pen.setColor(isEnabled() ? c.dark(130) : c); @@ -289,27 +289,27 @@ void AudioDial::drawTick(QPainter &paint, - float angle, int size, bool internal) + double angle, int size, bool internal) { - float hyp = float(size) / 2.0; - float x0 = hyp - (hyp - 1) * sin(angle); - float y0 = hyp + (hyp - 1) * cos(angle); + double hyp = double(size) / 2.0; + double x0 = hyp - (hyp - 1) * sin(angle); + double y0 = hyp + (hyp - 1) * cos(angle); // cerr << "drawTick: angle " << angle << ", size " << size << ", internal " << internal << endl; if (internal) { - float len = hyp / 4; - float x1 = hyp - (hyp - len) * sin(angle); - float y1 = hyp + (hyp - len) * cos(angle); + double len = hyp / 4; + double x1 = hyp - (hyp - len) * sin(angle); + double y1 = hyp + (hyp - len) * cos(angle); paint.drawLine(int(x0), int(y0), int(x1), int(y1)); } else { - float len = hyp / 4; - float x1 = hyp - (hyp + len) * sin(angle); - float y1 = hyp + (hyp + len) * cos(angle); + double len = hyp / 4; + double x1 = hyp - (hyp + len) * sin(angle); + double y1 = hyp + (hyp + len) * cos(angle); paint.drawLine(int(x0), int(y0), int(x1), int(y1)); } @@ -350,7 +350,7 @@ updateMappedValue(value); } -void AudioDial::setDefaultMappedValue(float value) +void AudioDial::setDefaultMappedValue(double value) { m_defaultMappedValue = value; if (m_rangeMapper) { @@ -358,7 +358,7 @@ } } -void AudioDial::setMappedValue(float mappedValue) +void AudioDial::setMappedValue(double mappedValue) { if (m_rangeMapper) { int newPosition = m_rangeMapper->getPositionForValue(mappedValue); @@ -387,7 +387,7 @@ } -float AudioDial::mappedValue() const +double AudioDial::mappedValue() const { if (m_rangeMapper) { // SVDEBUG << "AudioDial::mappedValue(): value = " << value() << ", mappedValue = " << m_mappedValue << endl; @@ -464,11 +464,11 @@ if (m_rangeMapper) { - float min = m_rangeMapper->getValueForPosition(minimum()); - float max = m_rangeMapper->getValueForPosition(maximum()); + double min = m_rangeMapper->getValueForPosition(minimum()); + double max = m_rangeMapper->getValueForPosition(maximum()); if (min > max) { - float tmp = min; + double tmp = min; min = max; max = tmp; } @@ -494,7 +494,7 @@ } } - float newValue = QInputDialog::getDouble + double newValue = QInputDialog::getDouble (this, tr("Enter new value"), text, diff -r 282f4be8f058 -r c02c51ae5238 widgets/AudioDial.h --- a/widgets/AudioDial.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/AudioDial.h Wed Apr 20 12:06:28 2016 +0100 @@ -73,7 +73,7 @@ void setRangeMapper(RangeMapper *mapper); // I take ownership, will delete const RangeMapper *rangeMapper() const { return m_rangeMapper; } - float mappedValue() const; + double mappedValue() const; int defaultValue() const { return m_defaultValue; } @@ -107,14 +107,14 @@ void setValue(int value); - void setDefaultMappedValue(float mappedValue); + void setDefaultMappedValue(double mappedValue); - void setMappedValue(float mappedValue); + void setMappedValue(double mappedValue); void setToDefault(); protected: - void drawTick(QPainter &paint, float angle, int size, bool internal); + void drawTick(QPainter &paint, double angle, int size, bool internal); virtual void paintEvent(QPaintEvent *); // Alternate mouse behavior event handlers. @@ -133,8 +133,8 @@ QColor m_meterColor; int m_defaultValue; - float m_defaultMappedValue; - float m_mappedValue; + double m_defaultMappedValue; + double m_mappedValue; bool m_noMappedUpdate; // Alternate mouse behavior tracking. diff -r 282f4be8f058 -r c02c51ae5238 widgets/CSVFormatDialog.cpp --- a/widgets/CSVFormatDialog.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/CSVFormatDialog.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -30,6 +30,7 @@ #include #include +#include #include "base/Debug.h" @@ -62,7 +63,7 @@ exampleFrame->setPalette(palette); QFont fp; - fp.setPointSize(fp.pointSize() * 0.9); + fp.setPointSize(int(floor(fp.pointSize() * 0.9))); // fp.setFixedPitch(true); // fp.setStyleHint(QFont::TypeWriter); // fp.setFamily("Monospaced"); @@ -94,10 +95,14 @@ cpc->addItem(tr("End time")); // ColumnEndTime cpc->addItem(tr("Duration")); // ColumnDuration cpc->addItem(tr("Value")); // ColumnValue + cpc->addItem(tr("Pitch")); // ColumnPitch cpc->addItem(tr("Label")); // ColumnLabel cpc->setCurrentIndex(int(m_format.getColumnPurpose(i))); for (int j = 0; j < example.size() && j < 6; ++j) { + if (i >= example[j].size()) { + continue; + } QLabel *label = new QLabel; label->setTextFormat(Qt::PlainText); QString text = TextAbbrev::abbreviate(example[j][i], 35); @@ -116,26 +121,48 @@ layout->addWidget(new QLabel(tr("Timing is specified:")), row, 0); m_timingTypeCombo = new QComboBox; - m_timingTypeCombo->addItem(tr("Explicitly, in seconds")); - m_timingTypeCombo->addItem(tr("Explicitly, in audio sample frames")); - m_timingTypeCombo->addItem(tr("Implicitly: rows are equally spaced in time")); + + m_timingLabels = { + { TimingExplicitSeconds, tr("Explicitly, in seconds") }, + { TimingExplicitMsec, tr("Explicitly, in milliseconds") }, + { TimingExplicitSamples, tr("Explicitly, in audio sample frames") }, + { TimingImplicit, tr("Implicitly: rows are equally spaced in time") } + }; + + for (auto &l: m_timingLabels) { + m_timingTypeCombo->addItem(l.second); + } + layout->addWidget(m_timingTypeCombo, row++, 1, 1, 2); + connect(m_timingTypeCombo, SIGNAL(activated(int)), this, SLOT(timingTypeChanged(int))); - m_timingTypeCombo->setCurrentIndex - (m_format.getTimingType() == CSVFormat::ExplicitTiming ? - m_format.getTimeUnits() == CSVFormat::TimeSeconds ? 0 : 1 : 2); + + m_initialTimingOption = TimingImplicit; + if (m_format.getTimingType() == CSVFormat::ExplicitTiming) { + switch (m_format.getTimeUnits()) { + case CSVFormat::TimeSeconds: + m_initialTimingOption = TimingExplicitSeconds; break; + case CSVFormat::TimeMilliseconds: + m_initialTimingOption = TimingExplicitMsec; break; + case CSVFormat::TimeAudioFrames: + m_initialTimingOption = TimingExplicitSamples; break; + case CSVFormat::TimeWindows: + m_initialTimingOption = TimingImplicit; break; + } + } + m_timingTypeCombo->setCurrentIndex(int(m_initialTimingOption)); m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):")); layout->addWidget(m_sampleRateLabel, row, 0); - size_t sampleRates[] = { + int sampleRates[] = { 8000, 11025, 12000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; m_sampleRateCombo = new QComboBox; - for (size_t i = 0; i < sizeof(sampleRates) / sizeof(sampleRates[0]); ++i) { + for (int i = 0; i < int(sizeof(sampleRates) / sizeof(sampleRates[0])); ++i) { m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i])); if (sampleRates[i] == m_format.getSampleRate()) { m_sampleRateCombo->setCurrentIndex(i); @@ -183,7 +210,6 @@ setLayout(layout); timingTypeChanged(m_timingTypeCombo->currentIndex()); - updateModelLabel(); } CSVFormatDialog::~CSVFormatDialog() @@ -212,6 +238,9 @@ case CSVFormat::TwoDimensionalModelWithDuration: s = f->getLayerPresentationName(LayerFactory::Regions); break; + case CSVFormat::TwoDimensionalModelWithDurationAndPitch: + s = f->getLayerPresentationName(LayerFactory::Notes); + break; case CSVFormat::ThreeDimensionalModel: s = f->getLayerPresentationName(LayerFactory::Colour3DPlot); break; @@ -221,37 +250,72 @@ } void +CSVFormatDialog::applyStartTimePurpose() +{ + // First check if we already have any. NB there may be fewer than + // m_format.getColumnCount() elements in m_columnPurposeCombos + // (because of the fuzzy column behaviour). Note also that the + // fuzzy column (which is the one just showing how many more + // columns there are) has a different combo with only two items + // (ignore or Values) + for (int i = 0; i < m_columnPurposeCombos.size(); ++i) { + if (i == m_fuzzyColumn) continue; + QComboBox *cb = m_columnPurposeCombos[i]; + if (cb->currentIndex() == int(CSVFormat::ColumnStartTime)) { + return; + } + } + // and if not, select one + for (int i = 0; i < m_columnPurposeCombos.size(); ++i) { + if (i == m_fuzzyColumn) continue; + QComboBox *cb = m_columnPurposeCombos[i]; + if (cb->currentIndex() == int(CSVFormat::ColumnValue)) { + cb->setCurrentIndex(int(CSVFormat::ColumnStartTime)); + return; + } + } +} + +void +CSVFormatDialog::removeStartTimePurpose() +{ + // NB there may be fewer than m_format.getColumnCount() elements + // in m_columnPurposeCombos (because of the fuzzy column + // behaviour) + for (int i = 0; i < m_columnPurposeCombos.size(); ++i) { + if (i == m_fuzzyColumn) continue; + QComboBox *cb = m_columnPurposeCombos[i]; + if (cb->currentIndex() == int(CSVFormat::ColumnStartTime)) { + cb->setCurrentIndex(int(CSVFormat::ColumnValue)); + } + } +} + +void +CSVFormatDialog::updateComboVisibility() +{ + bool wantRate = (m_format.getTimingType() == CSVFormat::ImplicitTiming || + m_format.getTimeUnits() == CSVFormat::TimeAudioFrames); + bool wantWindow = (m_format.getTimingType() == CSVFormat::ImplicitTiming); + + m_sampleRateCombo->setEnabled(wantRate); + m_sampleRateLabel->setEnabled(wantRate); + + m_windowSizeCombo->setEnabled(wantWindow); + m_windowSizeLabel->setEnabled(wantWindow); +} + +void CSVFormatDialog::timingTypeChanged(int type) { - switch (type) { - - case 0: - m_format.setTimingType(CSVFormat::ExplicitTiming); - m_format.setTimeUnits(CSVFormat::TimeSeconds); - m_sampleRateCombo->setEnabled(false); - m_sampleRateLabel->setEnabled(false); - m_windowSizeCombo->setEnabled(false); - m_windowSizeLabel->setEnabled(false); - break; - - case 1: - m_format.setTimingType(CSVFormat::ExplicitTiming); - m_format.setTimeUnits(CSVFormat::TimeAudioFrames); - m_sampleRateCombo->setEnabled(true); - m_sampleRateLabel->setEnabled(true); - m_windowSizeCombo->setEnabled(false); - m_windowSizeLabel->setEnabled(false); - break; - - case 2: - m_format.setTimingType(CSVFormat::ImplicitTiming); - m_format.setTimeUnits(CSVFormat::TimeWindows); - m_sampleRateCombo->setEnabled(true); - m_sampleRateLabel->setEnabled(true); - m_windowSizeCombo->setEnabled(true); - m_windowSizeLabel->setEnabled(true); - break; + // Update any column purpose combos + if (TimingOption(type) == TimingImplicit) { + removeStartTimePurpose(); + } else { + applyStartTimePurpose(); } + updateFormatFromDialog(); + updateComboVisibility(); } void @@ -274,44 +338,31 @@ CSVFormatDialog::columnPurposeChanged(int p) { QObject *o = sender(); - QComboBox *cb = qobject_cast(o); if (!cb) return; CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose)p; - bool haveStartTime = false; - bool haveDuration = false; - int valueCount = 0; - + bool haveStartTime = false; // so as to update timing type combo appropriately + + // Ensure the column purpose combos are consistent with one + // another, without reference to m_format (which we'll update + // separately) + for (int i = 0; i < m_columnPurposeCombos.size(); ++i) { - CSVFormat::ColumnPurpose cp = m_format.getColumnPurpose(i); + // The fuzzy column combo only has the entries or + // Values, so it can't affect the timing type and none of this + // logic affects it + if (i == m_fuzzyColumn) continue; - bool thisChanged = (cb == m_columnPurposeCombos[i]); + QComboBox *thisCombo = m_columnPurposeCombos[i]; - if (thisChanged) { - - cerr << "i == " << i << ", fuzzy == " << m_fuzzyColumn - << ", p == " << p << endl; - - if (i == m_fuzzyColumn) { - for (int j = i; j < m_format.getColumnCount(); ++j) { - if (p == 0) { // Ignore - m_format.setColumnPurpose(j, CSVFormat::ColumnUnknown); - } else { // Value - m_format.setColumnPurpose(j, CSVFormat::ColumnValue); - ++valueCount; - } - } - continue; - } - - cp = purpose; - - } else { - - if (i == m_fuzzyColumn) continue; + CSVFormat::ColumnPurpose cp = (CSVFormat::ColumnPurpose) + (thisCombo->currentIndex()); + bool thisChanged = (cb == thisCombo); + + if (!thisChanged) { // We can only have one ColumnStartTime column, and only // one of either ColumnDuration or ColumnEndTime @@ -334,30 +385,107 @@ cp = CSVFormat::ColumnUnknown; } } + + if (cp == CSVFormat::ColumnStartTime) { + haveStartTime = true; + } + + thisCombo->setCurrentIndex(int(cp)); + + } else { + if (purpose == CSVFormat::ColumnStartTime) { + haveStartTime = true; + } } - - if (cp == CSVFormat::ColumnStartTime) { - haveStartTime = true; - } - if (cp == CSVFormat::ColumnEndTime || - cp == CSVFormat::ColumnDuration) { - haveDuration = true; - } - if (cp == CSVFormat::ColumnValue) { - ++valueCount; - } - - m_columnPurposeCombos[i]->setCurrentIndex(int(cp)); - m_format.setColumnPurpose(i, cp); } if (!haveStartTime) { - m_timingTypeCombo->setCurrentIndex(2); - timingTypeChanged(2); + m_timingTypeCombo->setCurrentIndex(int(TimingImplicit)); + } else if (m_timingTypeCombo->currentIndex() == int(TimingImplicit)) { + if (m_initialTimingOption == TimingImplicit) { + m_timingTypeCombo->setCurrentIndex(TimingExplicitSeconds); + } else { + m_timingTypeCombo->setCurrentIndex(m_initialTimingOption); + } + } + + updateFormatFromDialog(); + updateComboVisibility(); +} + +void +CSVFormatDialog::updateFormatFromDialog() +{ + switch (TimingOption(m_timingTypeCombo->currentIndex())) { + + case TimingExplicitSeconds: + m_format.setTimingType(CSVFormat::ExplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeSeconds); + break; + + case TimingExplicitMsec: + m_format.setTimingType(CSVFormat::ExplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeMilliseconds); + break; + + case TimingExplicitSamples: + m_format.setTimingType(CSVFormat::ExplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeAudioFrames); + break; + + case TimingImplicit: + m_format.setTimingType(CSVFormat::ImplicitTiming); + m_format.setTimeUnits(CSVFormat::TimeWindows); + break; + } + + bool haveStartTime = false; + bool haveDuration = false; + bool havePitch = false; + int valueCount = 0; + + for (int i = 0; i < m_columnPurposeCombos.size(); ++i) { + + QComboBox *thisCombo = m_columnPurposeCombos[i]; + + CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose) + (thisCombo->currentIndex()); + + if (i == m_fuzzyColumn) { + for (int j = i; j < m_format.getColumnCount(); ++j) { + if (purpose == CSVFormat::ColumnUnknown) { + m_format.setColumnPurpose(j, CSVFormat::ColumnUnknown); + } else { // Value + m_format.setColumnPurpose(j, CSVFormat::ColumnValue); + ++valueCount; + } + } + } else { + + if (purpose == CSVFormat::ColumnStartTime) { + haveStartTime = true; + } + if (purpose == CSVFormat::ColumnEndTime || + purpose == CSVFormat::ColumnDuration) { + haveDuration = true; + } + if (purpose == CSVFormat::ColumnPitch) { + havePitch = true; + } + if (purpose == CSVFormat::ColumnValue) { + ++valueCount; + } + + m_format.setColumnPurpose(i, purpose); + } } if (haveStartTime && haveDuration) { - m_format.setModelType(CSVFormat::TwoDimensionalModelWithDuration); + if (havePitch) { + m_format.setModelType(CSVFormat::TwoDimensionalModelWithDurationAndPitch); + } else { + m_format.setModelType(CSVFormat::TwoDimensionalModelWithDuration); + } } else { if (valueCount > 1) { m_format.setModelType(CSVFormat::ThreeDimensionalModel); @@ -372,3 +500,4 @@ } + diff -r 282f4be8f058 -r c02c51ae5238 widgets/CSVFormatDialog.h --- a/widgets/CSVFormatDialog.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/CSVFormatDialog.h Wed Apr 20 12:06:28 2016 +0100 @@ -40,11 +40,26 @@ void sampleRateChanged(QString); void windowSizeChanged(QString); void columnPurposeChanged(int purpose); + + void updateFormatFromDialog(); void updateModelLabel(); protected: CSVFormat m_format; int m_maxDisplayCols; + + enum TimingOption { + TimingExplicitSeconds = 0, + TimingExplicitMsec, + TimingExplicitSamples, + TimingImplicit + }; + std::map m_timingLabels; + TimingOption m_initialTimingOption; + + void updateComboVisibility(); + void applyStartTimePurpose(); + void removeStartTimePurpose(); QComboBox *m_timingTypeCombo; QLabel *m_sampleRateLabel; diff -r 282f4be8f058 -r c02c51ae5238 widgets/CommandHistory.cpp --- a/widgets/CommandHistory.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/CommandHistory.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -26,6 +26,8 @@ #include "base/Command.h" +#include "IconLoader.h" + #include #include #include @@ -53,12 +55,16 @@ m_bundleTimer(0), m_bundleTimeout(3000) { - m_undoAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this); + IconLoader loader; + QIcon undoIcon(loader.load("undo")); + QIcon redoIcon(loader.load("redo")); + + m_undoAction = new QAction(undoIcon, ("&Undo"), this); m_undoAction->setShortcut(tr("Ctrl+Z")); m_undoAction->setStatusTip(tr("Undo the last editing operation")); connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo())); - m_undoMenuAction = new QAction(QIcon(":/icons/undo.png"), tr("&Undo"), this); + m_undoMenuAction = new QAction(undoIcon, tr("&Undo"), this); connect(m_undoMenuAction, SIGNAL(triggered()), this, SLOT(undo())); m_undoMenu = new QMenu(tr("&Undo")); @@ -66,12 +72,12 @@ connect(m_undoMenu, SIGNAL(triggered(QAction *)), this, SLOT(undoActivated(QAction*))); - m_redoAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this); + m_redoAction = new QAction(redoIcon, tr("Re&do"), this); m_redoAction->setShortcut(tr("Ctrl+Shift+Z")); m_redoAction->setStatusTip(tr("Redo the last operation that was undone")); connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo())); - m_redoMenuAction = new QAction(QIcon(":/icons/redo.png"), tr("Re&do"), this); + m_redoMenuAction = new QAction(redoIcon, tr("Re&do"), this); connect(m_redoMenuAction, SIGNAL(triggered()), this, SLOT(redo())); m_redoMenu = new QMenu(tr("Re&do")); @@ -101,7 +107,7 @@ CommandHistory::clear() { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::clear()" << endl; + cerr << "CommandHistory::clear()" << endl; #endif closeBundle(); m_savedAt = -1; @@ -143,7 +149,7 @@ if (!command) return; #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addCommand: " << command->getName() << " of type " << typeid(*command).name() << " at " << command << ": execute = " << execute << ", bundle = " << bundle << " (m_currentCompound = " << m_currentCompound << ", m_currentBundle = " << m_currentBundle << ")" << endl; + cerr << "CommandHistory::addCommand: " << command->getName() << " of type " << typeid(*command).name() << " at " << command << ": execute = " << execute << ", bundle = " << bundle << " (m_currentCompound = " << m_currentCompound << ", m_currentBundle = " << m_currentBundle << ")" << endl; #endif if (m_currentCompound) { @@ -160,7 +166,7 @@ #ifdef DEBUG_COMMAND_HISTORY if (!m_redoStack.empty()) { - SVDEBUG << "CommandHistory::clearing redo stack" << endl; + cerr << "CommandHistory::clearing redo stack" << endl; } #endif @@ -192,8 +198,8 @@ if (m_currentBundle) { if (!command || (command->getName() != m_currentBundleName)) { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addToBundle: " - << command->getName() << ": closing current bundle" << endl; + cerr << "CommandHistory::addToBundle: " << command->getName() + << ": closing current bundle" << endl; #endif closeBundle(); } @@ -204,8 +210,8 @@ if (!m_currentBundle) { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addToBundle: " - << command->getName() << ": creating new bundle" << endl; + cerr << "CommandHistory::addToBundle: " << command->getName() + << ": creating new bundle" << endl; #endif // need to addCommand before setting m_currentBundle, as addCommand @@ -219,8 +225,8 @@ } #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addToBundle: " - << command->getName() << ": adding to bundle" << endl; + cerr << "CommandHistory::addToBundle: " << command->getName() + << ": adding to bundle" << endl; #endif if (execute) command->execute(); @@ -242,11 +248,12 @@ void CommandHistory::closeBundle() { + if (m_currentBundle) { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::closeBundle" << endl; + cerr << "CommandHistory::closeBundle" << endl; #endif - - if (m_currentBundle) emit activity(m_currentBundle->getName()); + emit activity(m_currentBundle->getName()); + } m_currentBundle = 0; m_currentBundleName = ""; } @@ -255,7 +262,7 @@ CommandHistory::bundleTimerTimeout() { #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::bundleTimerTimeout: bundle is " << m_currentBundle << endl; + cerr << "CommandHistory::bundleTimerTimeout: bundle is " << m_currentBundle << endl; #endif closeBundle(); @@ -264,14 +271,15 @@ void CommandHistory::addToCompound(Command *command, bool execute) { -#ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::addToCompound: " << command->getName() << endl; -#endif if (!m_currentCompound) { - SVDEBUG << "CommandHistory::addToCompound: ERROR: no compound operation in progress!" << endl; + cerr << "CommandHistory::addToCompound: ERROR: no compound operation in progress!" << endl; return; } +#ifdef DEBUG_COMMAND_HISTORY + cerr << "CommandHistory::addToCompound[" << m_currentCompound->getName() << "]: " << command->getName() << " (exec: " << execute << ")" << endl; +#endif + if (execute) command->execute(); m_currentCompound->addCommand(command); } @@ -280,13 +288,17 @@ CommandHistory::startCompoundOperation(QString name, bool execute) { if (m_currentCompound) { - SVDEBUG << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << endl; + cerr << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << endl; cerr << "(name is " << m_currentCompound->getName() << ")" << endl; return; } +#ifdef DEBUG_COMMAND_HISTORY + cerr << "CommandHistory::startCompoundOperation: " << name << " (exec: " << execute << ")" << endl; +#endif + closeBundle(); - + m_currentCompound = new MacroCommand(name); m_executeCompound = execute; } @@ -295,9 +307,13 @@ CommandHistory::endCompoundOperation() { if (!m_currentCompound) { - SVDEBUG << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << endl; + cerr << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << endl; return; } + +#ifdef DEBUG_COMMAND_HISTORY + cerr << "CommandHistory::endCompoundOperation: " << m_currentCompound->getName() << endl; +#endif MacroCommand *toAdd = m_currentCompound; m_currentCompound = 0; @@ -329,7 +345,7 @@ if (m_undoStack.empty()) return; #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::undo()" << endl; + cerr << "CommandHistory::undo()" << endl; #endif closeBundle(); @@ -355,7 +371,7 @@ if (m_redoStack.empty()) return; #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::redo()" << endl; + cerr << "CommandHistory::redo()" << endl; #endif closeBundle(); @@ -410,14 +426,14 @@ CommandHistory::documentSaved() { closeBundle(); - m_savedAt = m_undoStack.size(); + m_savedAt = int(m_undoStack.size()); } void CommandHistory::clipCommands() { - if ((int)m_undoStack.size() > m_undoLimit) { - m_savedAt -= (m_undoStack.size() - m_undoLimit); + if (int(m_undoStack.size()) > m_undoLimit) { + m_savedAt -= (int(m_undoStack.size()) - m_undoLimit); } clipStack(m_undoStack, m_undoLimit); @@ -436,7 +452,7 @@ for (i = 0; i < limit; ++i) { #ifdef DEBUG_COMMAND_HISTORY Command *command = stack.top(); - SVDEBUG << "CommandHistory::clipStack: Saving recent command: " << command->getName() << " at " << command << endl; + cerr << "CommandHistory::clipStack: Saving recent command: " << command->getName() << " at " << command << endl; #endif tempStack.push(stack.top()); stack.pop(); @@ -458,7 +474,7 @@ Command *command = stack.top(); // Not safe to call getName() on a command about to be deleted #ifdef DEBUG_COMMAND_HISTORY - SVDEBUG << "CommandHistory::clearStack: About to delete command " << command << endl; + cerr << "CommandHistory::clearStack: About to delete command " << command << endl; #endif delete command; stack.pop(); diff -r 282f4be8f058 -r c02c51ae5238 widgets/Fader.cpp --- a/widgets/Fader.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/Fader.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -48,7 +48,9 @@ m_value(1.0), m_peakLeft(0.0), m_peakRight(0.0), - m_mousePressed(false) + m_mousePressed(false), + m_mousePressX(0), + m_mousePressValue(0) { setMinimumSize(116, 23); setMaximumSize(116, 23); @@ -108,7 +110,7 @@ if (vx > getMaxX()) vx = getMaxX(); if (vx < 0) vx = 0; - float fval = AudioLevel::fader_to_multiplier + float fval = (float)AudioLevel::fader_to_multiplier (vx, getMaxX(), AudioLevel::LongFader); setValue(fval); @@ -130,20 +132,20 @@ Fader::mouseDoubleClickEvent(QMouseEvent *) { bool ok = false; - float min = AudioLevel::fader_to_dB + float min = (float)AudioLevel::fader_to_dB (0, getMaxX(), AudioLevel::LongFader); - float max = AudioLevel::fader_to_dB + float max = (float)AudioLevel::fader_to_dB (getMaxX(), getMaxX(), AudioLevel::LongFader); - float deft = AudioLevel::multiplier_to_dB(m_value); + float deft = (float)AudioLevel::multiplier_to_dB(m_value); - float dB = QInputDialog::getDouble + float dB = (float)QInputDialog::getDouble (this, tr("Enter new fader level"), tr("New fader level, from %1 to %2 dBFS:").arg(min).arg(max), deft, min, max, 3, &ok); if (ok) { - float value = AudioLevel::dB_to_multiplier(dB); + float value = (float)AudioLevel::dB_to_multiplier(dB); setValue(value); emit valueChanged(value); update(); @@ -177,9 +179,9 @@ //!!! needs improvement if (ev->delta() > 0) { - setValue(m_value * 1.1); + setValue(m_value * 1.f); } else { - setValue(m_value / 1.1); + setValue(m_value / 1.f); } update(); @@ -201,7 +203,7 @@ void Fader::setValue(float v) { - float max = AudioLevel::dB_to_multiplier(10.0); + float max = (float)AudioLevel::dB_to_multiplier(10.0); if (v > max) { v = max; @@ -211,7 +213,7 @@ if (m_value != v) { m_value = v; - float db = AudioLevel::multiplier_to_dB(m_value); + float db = (float)AudioLevel::multiplier_to_dB(m_value); QString text; if (db <= AudioLevel::DB_FLOOR) { text = tr("Level: Off"); diff -r 282f4be8f058 -r c02c51ae5238 widgets/IconLoader.cpp --- a/widgets/IconLoader.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/IconLoader.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -19,8 +19,17 @@ #include #include #include +#include +#include -static const char *autoInvertExceptions[] = { +#include +#include + +#include "base/Debug.h" + +using namespace std; + +static set autoInvertExceptions { // These are the icons that look OK in their default colours, even // in a colour scheme with a black background. (They may also be // icons that would look worse if we tried to auto-invert them.) @@ -29,16 +38,13 @@ // supply inverted versions -- the loader will load xx_inverse.png // in preference to xx.png if a dark background is found.) "fileclose", - "filenew-22", "filenew", - "fileopen-22", "fileopen", "fileopenaudio", "fileopensession", - "filesave-22", "filesave", - "filesaveas-22", "filesaveas", + "filesaveas-sv", "help", "editcut", "editcopy", @@ -51,42 +57,121 @@ "zoom" }; +static vector sizes { 0, 16, 22, 24, 32, 48, 64, 128 }; + QIcon IconLoader::load(QString name) { - QPixmap pmap(loadPixmap(name)); - if (pmap.isNull()) return QIcon(); - else return QIcon(pmap); + QIcon icon; + for (int sz: sizes) { + QPixmap pmap(loadPixmap(name, sz)); + if (!pmap.isNull()) icon.addPixmap(pmap); + } + return icon; +} + +bool +IconLoader::shouldInvert() const +{ + QColor bg = QApplication::palette().window().color(); + bool darkBackground = (bg.red() + bg.green() + bg.blue() <= 384); + return darkBackground; +} + +bool +IconLoader::shouldAutoInvert(QString name) const +{ + if (shouldInvert()) { + return (autoInvertExceptions.find(name) == autoInvertExceptions.end()); + } else { + return false; + } } QPixmap -IconLoader::loadPixmap(QString name) +IconLoader::loadPixmap(QString name, int size) { - QColor bg = QApplication::palette().window().color(); - if (bg.red() + bg.green() + bg.blue() > 384) { // light background - QPixmap pmap(QString(":icons/%1").arg(name)); - if (pmap.isNull()) { - pmap = QPixmap(QString(":icons/%1.png").arg(name)); - } - return pmap; + bool invert = shouldInvert(); + + QString scalableName, nonScalableName; + QPixmap pmap; + + nonScalableName = makeNonScalableFilename(name, size, invert); + pmap = QPixmap(nonScalableName); + if (!pmap.isNull()) return pmap; + + if (size > 0) { + scalableName = makeScalableFilename(name, invert); + pmap = loadScalable(scalableName, size); + if (!pmap.isNull()) return pmap; } - QPixmap pmap(QString(":icons/%1").arg(name)); - if (pmap.isNull()) { - pmap = QPixmap(QString(":icons/%1_inverse.png").arg(name)); - if (pmap.isNull()) { - pmap = QPixmap(QString(":icons/%1.png").arg(name)); - } - } - if (pmap.isNull()) return pmap; + if (invert && shouldAutoInvert(name)) { - for (int i = 0; i < sizeof(autoInvertExceptions)/ - sizeof(autoInvertExceptions[0]); ++i) { - if (autoInvertExceptions[i] == name) { - return pmap; + nonScalableName = makeNonScalableFilename(name, size, false); + pmap = QPixmap(nonScalableName); + if (!pmap.isNull()) return invertPixmap(pmap); + + if (size > 0) { + scalableName = makeScalableFilename(name, false); + pmap = loadScalable(scalableName, size); + if (!pmap.isNull()) return invertPixmap(pmap); } } + return QPixmap(); +} + +QPixmap +IconLoader::loadScalable(QString name, int size) +{ + if (!QFile(name).exists()) { +// cerr << "loadScalable: no such file as: \"" << name << "\"" << endl; + return QPixmap(); + } + QPixmap pmap(size, size); + pmap.fill(Qt::transparent); + QSvgRenderer renderer(name); + QPainter painter; + painter.begin(&pmap); +// cerr << "calling renderer for " << name << " at size " << size << "..." << endl; + renderer.render(&painter); +// cerr << "renderer completed" << endl; + painter.end(); + return pmap; +} + +QString +IconLoader::makeNonScalableFilename(QString name, int size, bool invert) +{ + if (invert) { + if (size == 0) { + return QString(":icons/%1_inverse.png").arg(name); + } else { + return QString(":icons/%1-%2_inverse.png").arg(name).arg(size); + } + } else { + if (size == 0) { + return QString(":icons/%1.png").arg(name); + } else { + return QString(":icons/%1-%2.png").arg(name).arg(size); + } + } +} + +QString +IconLoader::makeScalableFilename(QString name, bool invert) +{ + if (invert) { + return QString(":icons/scalable/%1_inverse.svg").arg(name); + } else { + return QString(":icons/scalable/%1.svg").arg(name); + } +} + +QPixmap +IconLoader::invertPixmap(QPixmap pmap) +{ // No suitable inverted icon found for black background; try to // auto-invert the default one diff -r 282f4be8f058 -r c02c51ae5238 widgets/IconLoader.h --- a/widgets/IconLoader.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/IconLoader.h Wed Apr 20 12:06:28 2016 +0100 @@ -25,7 +25,15 @@ virtual ~IconLoader() { } QIcon load(QString name); - QPixmap loadPixmap(QString name); + +private: + bool shouldInvert() const; + bool shouldAutoInvert(QString) const; + QPixmap loadPixmap(QString, int); + QPixmap loadScalable(QString, int); + QPixmap invertPixmap(QPixmap); + QString makeScalableFilename(QString, bool); + QString makeNonScalableFilename(QString, int, bool); }; #endif diff -r 282f4be8f058 -r c02c51ae5238 widgets/InteractiveFileFinder.cpp --- a/widgets/InteractiveFileFinder.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/InteractiveFileFinder.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -33,7 +33,9 @@ InteractiveFileFinder::m_instance; InteractiveFileFinder::InteractiveFileFinder() : - m_lastLocatedLocation("") + m_sessionExtension("sv"), + m_lastLocatedLocation(""), + m_parent(0) { SVDEBUG << "Registering interactive file finder" << endl; FileFinder::registerFileFinder(this); @@ -43,10 +45,22 @@ { } +void +InteractiveFileFinder::setParentWidget(QWidget *parent) +{ + getInstance()->m_parent = parent; +} + +void +InteractiveFileFinder::setApplicationSessionExtension(QString extension) +{ + m_sessionExtension = extension; +} + QString InteractiveFileFinder::getOpenFileName(FileType type, QString fallbackLocation) { - QString settingsKey; + QString settingsKeyStub; QString lastPath = fallbackLocation; QString title = tr("Select file"); @@ -55,41 +69,60 @@ switch (type) { case SessionFile: - settingsKey = "sessionpath"; + settingsKeyStub = "session"; title = tr("Select a session file"); - filter = tr("Sonic Visualiser session files (*.sv)\nRDF files (%1)\nAll files (*.*)").arg(RDFImporter::getKnownExtensions()); + filter = tr("%1 session files (*.%1)\nRDF files (%3)\nAll files (*.*)") + .arg(QApplication::applicationName()) + .arg(m_sessionExtension) + .arg(RDFImporter::getKnownExtensions()); break; case AudioFile: - settingsKey = "audiopath"; + settingsKeyStub = "audio"; title = "Select an audio file"; filter = tr("Audio files (%1)\nAll files (*.*)") .arg(AudioFileReaderFactory::getKnownExtensions()); break; case LayerFile: - settingsKey = "layerpath"; + settingsKeyStub = "layer"; filter = tr("All supported files (%1 %2)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)") .arg(DataFileReaderFactory::getKnownExtensions()) .arg(RDFImporter::getKnownExtensions()); break; case LayerFileNoMidi: - settingsKey = "layerpath"; + settingsKeyStub = "layer"; filter = tr("All supported files (%1 %2)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nText files (*.txt)\nAll files (*.*)") .arg(DataFileReaderFactory::getKnownExtensions()) .arg(RDFImporter::getKnownExtensions()); break; + case LayerFileNonSV: + settingsKeyStub = "layer"; + filter = tr("All supported files (%1 %2)\nComma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)") + .arg(DataFileReaderFactory::getKnownExtensions()) + .arg(RDFImporter::getKnownExtensions()); + break; + + case LayerFileNoMidiNonSV: + settingsKeyStub = "layer"; + filter = tr("All supported files (%1 %2)\nComma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nSpace-separated .lab files (*.lab)\nRDF files (%2)\nText files (*.txt)\nAll files (*.*)") + .arg(DataFileReaderFactory::getKnownExtensions()) + .arg(RDFImporter::getKnownExtensions()); + break; + case SessionOrAudioFile: - settingsKey = "lastpath"; - filter = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%2)\nRDF files (%1)\nAll files (*.*)") + settingsKeyStub = "last"; + filter = tr("All supported files (*.sv %1 %2)\n%3 session files (*.%4)\nAudio files (%2)\nRDF files (%1)\nAll files (*.*)") .arg(RDFImporter::getKnownExtensions()) - .arg(AudioFileReaderFactory::getKnownExtensions()); + .arg(AudioFileReaderFactory::getKnownExtensions()) + .arg(QApplication::applicationName()) + .arg(m_sessionExtension); break; case ImageFile: - settingsKey = "imagepath"; + settingsKeyStub = "image"; { QStringList fmts; QList formats = QImageReader::supportedImageFormats(); @@ -103,22 +136,24 @@ break; case CSVFile: - settingsKey = "layerpath"; + settingsKeyStub = "layer"; filter = tr("Comma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nText files (*.txt)\nAll files (*.*)"); break; case IMAFile: - settingsKey = "imafpath"; + settingsKeyStub = "imaf"; title = "Select an IMAF file"; filter = tr("IMAF files (*.ima)\nAll files (*.*)"); break; case AnyFile: - settingsKey = "lastpath"; - filter = tr("All supported files (*.sv %1 %2 %3)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nRDF files (%3)\nAll files (*.*)") + settingsKeyStub = "last"; + filter = tr("All supported files (*.sv %1 %2 %3)\n%4 session files (*.%5)\nAudio files (%1)\nLayer files (%2)\nRDF files (%3)\nAll files (*.*)") .arg(AudioFileReaderFactory::getKnownExtensions()) .arg(DataFileReaderFactory::getKnownExtensions()) - .arg(RDFImporter::getKnownExtensions()); + .arg(RDFImporter::getKnownExtensions()) + .arg(QApplication::applicationName()) + .arg(m_sessionExtension); break; }; @@ -134,13 +169,13 @@ QSettings settings; settings.beginGroup("FileFinder"); - lastPath = settings.value(settingsKey, lastPath).toString(); + lastPath = settings.value(settingsKeyStub + "path", lastPath).toString(); QString path = ""; // Use our own QFileDialog just for symmetry with getSaveFileName below - QFileDialog dialog; + QFileDialog dialog(m_parent); dialog.setNameFilters(filter.split('\n')); dialog.setWindowTitle(title); dialog.setDirectory(lastPath); @@ -187,7 +222,7 @@ } if (path != "") { - settings.setValue(settingsKey, + settings.setValue(settingsKeyStub + "path", QFileInfo(path).absoluteDir().canonicalPath()); } @@ -195,9 +230,10 @@ } QString -InteractiveFileFinder::getSaveFileName(FileType type, QString fallbackLocation) +InteractiveFileFinder::getSaveFileName(FileType type, + QString fallbackLocation) { - QString settingsKey; + QString settingsKeyStub; QString lastPath = fallbackLocation; QString title = tr("Select file"); @@ -206,46 +242,63 @@ switch (type) { case SessionFile: - settingsKey = "savesessionpath"; + settingsKeyStub = "savesession"; title = tr("Select a session file"); - filter = tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)"); + filter = tr("%1 session files (*.%2)\nAll files (*.*)") + .arg(QApplication::applicationName()).arg(m_sessionExtension); break; case AudioFile: - settingsKey = "saveaudiopath"; + settingsKeyStub = "saveaudio"; title = "Select an audio file"; title = tr("Select a file to export to"); filter = tr("WAV audio files (*.wav)\nAll files (*.*)"); break; case LayerFile: - settingsKey = "savelayerpath"; + settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nRDF/Turtle files (%1)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions()); break; case LayerFileNoMidi: - settingsKey = "savelayerpath"; + settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); filter = tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nRDF/Turtle files (%1)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions()); break; + case LayerFileNonSV: + settingsKeyStub = "savelayer"; + title = tr("Select a file to export to"); + filter = tr("Comma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nRDF/Turtle files (%1)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions()); + break; + + case LayerFileNoMidiNonSV: + settingsKeyStub = "savelayer"; + title = tr("Select a file to export to"); + filter = tr("Comma-separated data files (*.csv)\nSonic Visualiser Layer XML files (*.svl)\nRDF/Turtle files (%1)\nText files (*.txt)\nAll files (*.*)").arg(RDFExporter::getSupportedExtensions()); + break; + case SessionOrAudioFile: cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: SessionOrAudioFile cannot be used here" << endl; abort(); case ImageFile: - settingsKey = "saveimagepath"; + settingsKeyStub = "saveimage"; title = tr("Select a file to export to"); filter = tr("Portable Network Graphics files (*.png)\nAll files (*.*)"); break; case CSVFile: - settingsKey = "savelayerpath"; + settingsKeyStub = "savelayer"; title = tr("Select a file to export to"); filter = tr("Comma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)"); break; + case IMAFile: + cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: IMAFile cannot be used here" << endl; + abort(); + case AnyFile: cerr << "ERROR: Internal error: InteractiveFileFinder::getSaveFileName: AnyFile cannot be used here" << endl; abort(); @@ -263,30 +316,44 @@ QSettings settings; settings.beginGroup("FileFinder"); - lastPath = settings.value(settingsKey, lastPath).toString(); + lastPath = settings.value(settingsKeyStub + "path", lastPath).toString(); QString path = ""; // Use our own QFileDialog instead of static functions, as we may // need to adjust the file extension based on the selected filter - QFileDialog dialog; - dialog.setNameFilters(filter.split('\n')); + QFileDialog dialog(m_parent); + + QStringList filters = filter.split('\n'); + + dialog.setNameFilters(filters); dialog.setWindowTitle(title); dialog.setDirectory(lastPath); - dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setFileMode(QFileDialog::AnyFile); dialog.setConfirmOverwrite(false); // we'll do that - + + QString defaultSuffix; if (type == SessionFile) { - dialog.setDefaultSuffix("sv"); + defaultSuffix = m_sessionExtension; } else if (type == AudioFile) { - dialog.setDefaultSuffix("wav"); + defaultSuffix = "wav"; } else if (type == ImageFile) { - dialog.setDefaultSuffix("png"); + defaultSuffix = "png"; } else if (type == CSVFile) { - dialog.setDefaultSuffix("csv"); + defaultSuffix = "csv"; + } + + defaultSuffix = + settings.value(settingsKeyStub + "suffix", defaultSuffix).toString(); + + dialog.setDefaultSuffix(defaultSuffix); + + foreach (QString f, filters) { + if (f.contains("." + defaultSuffix)) { + dialog.selectNameFilter(f); + } } bool good = false; @@ -305,7 +372,8 @@ cerr << "type = " << type << ", suffix = " << fi.suffix() << endl; - if ((type == LayerFile || type == LayerFileNoMidi) + if ((type == LayerFile || type == LayerFileNoMidi || + type == LayerFileNonSV || type == LayerFileNoMidiNonSV) && fi.suffix() == "") { QString expectedExtension; QString selectedFilter = dialog.selectedNameFilter(); @@ -346,8 +414,10 @@ } if (path != "") { - settings.setValue(settingsKey, + settings.setValue(settingsKeyStub + "path", QFileInfo(path).absoluteDir().canonicalPath()); + settings.setValue(settingsKeyStub + "suffix", + QFileInfo(path).suffix()); } return path; @@ -356,39 +426,51 @@ void InteractiveFileFinder::registerLastOpenedFilePath(FileType type, QString path) { - QString settingsKey; + QString settingsKeyStub; switch (type) { case SessionFile: - settingsKey = "sessionpath"; + settingsKeyStub = "session"; break; case AudioFile: - settingsKey = "audiopath"; + settingsKeyStub = "audio"; break; case LayerFile: - settingsKey = "layerpath"; + settingsKeyStub = "layer"; break; case LayerFileNoMidi: - settingsKey = "layerpath"; + settingsKeyStub = "layer"; + break; + + case LayerFileNonSV: + settingsKeyStub = "layer"; + break; + + case LayerFileNoMidiNonSV: + settingsKeyStub = "layer"; break; case SessionOrAudioFile: - settingsKey = "lastpath"; + settingsKeyStub = "last"; break; case ImageFile: - settingsKey = "imagepath"; + settingsKeyStub = "image"; break; case CSVFile: - settingsKey = "layerpath"; + settingsKeyStub = "layer"; + break; + + case IMAFile: + settingsKeyStub = "imaf"; break; case AnyFile: - settingsKey = "lastpath"; + settingsKeyStub = "last"; break; } @@ -396,7 +478,9 @@ QSettings settings; settings.beginGroup("FileFinder"); path = QFileInfo(path).absoluteDir().canonicalPath(); - settings.setValue(settingsKey, path); + QString suffix = QFileInfo(path).suffix(); + settings.setValue(settingsKeyStub + "path", path); + settings.setValue(settingsKeyStub + "suffix", suffix); settings.setValue("lastpath", path); } } diff -r 282f4be8f058 -r c02c51ae5238 widgets/InteractiveFileFinder.h --- a/widgets/InteractiveFileFinder.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/InteractiveFileFinder.h Wed Apr 20 12:06:28 2016 +0100 @@ -18,6 +18,7 @@ #include "data/fileio/FileFinder.h" +#include #include #include @@ -29,12 +30,22 @@ public: virtual ~InteractiveFileFinder(); + /// Specify the extension for this application's session files + /// (without the dot) + void setApplicationSessionExtension(QString extension); + + QString getApplicationSessionExtension() const { + return m_sessionExtension; + } + QString getOpenFileName(FileType type, QString fallbackLocation = ""); QString getSaveFileName(FileType type, QString fallbackLocation = ""); void registerLastOpenedFilePath(FileType type, QString path); QString find(FileType type, QString location, QString lastKnownLocation = ""); + static void setParentWidget(QWidget *); + static InteractiveFileFinder *getInstance() { return &m_instance; } protected: @@ -44,7 +55,10 @@ QString findRelative(QString location, QString relativeTo); QString locateInteractive(FileType type, QString thing); + QString m_sessionExtension; QString m_lastLocatedLocation; + + QWidget *m_parent; }; #endif diff -r 282f4be8f058 -r c02c51ae5238 widgets/ItemEditDialog.cpp --- a/widgets/ItemEditDialog.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/ItemEditDialog.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -28,10 +28,13 @@ #include // for FLT_MIN/MAX -ItemEditDialog::ItemEditDialog(size_t sampleRate, int options, +ItemEditDialog::ItemEditDialog(sv_samplerate_t sampleRate, int options, QString valueUnits, QWidget *parent) : QDialog(parent), m_sampleRate(sampleRate), + m_defaultFrame(0), + m_defaultDuration(0), + m_defaultValue(0), m_frameTimeSpinBox(0), m_realTimeSecsSpinBox(0), m_realTimeUSecsSpinBox(0), @@ -50,7 +53,7 @@ int row = 0, subrow = 0; - size_t singleStep = RealTime::frame2RealTime(2, sampleRate).usec() - 1; + int singleStep = RealTime::frame2RealTime(2, sampleRate).usec() - 1; if ((options & ShowTime) || (options & ShowDuration)) { @@ -190,19 +193,19 @@ } void -ItemEditDialog::setFrameTime(long frame) +ItemEditDialog::setFrameTime(sv_frame_t frame) { if (!m_frameTimeSpinBox) return; RealTime rt(RealTime::frame2RealTime(frame, m_sampleRate)); m_realTimeSecsSpinBox->setValue(rt.sec); m_realTimeUSecsSpinBox->setValue(rt.usec()); - m_frameTimeSpinBox->setValue(frame); + m_frameTimeSpinBox->setValue(int(frame)); m_defaultFrame = frame; m_resetButton->setEnabled(false); } -long +sv_frame_t ItemEditDialog::getFrameTime() const { return m_frameTimeSpinBox->value(); @@ -221,19 +224,19 @@ } void -ItemEditDialog::setFrameDuration(long duration) +ItemEditDialog::setFrameDuration(sv_frame_t duration) { if (!m_frameDurationSpinBox) return; RealTime rt(RealTime::frame2RealTime(duration, m_sampleRate)); m_realDurationSecsSpinBox->setValue(rt.sec); m_realDurationUSecsSpinBox->setValue(rt.usec()); - m_frameDurationSpinBox->setValue(duration); + m_frameDurationSpinBox->setValue(int(duration)); m_defaultDuration = duration; m_resetButton->setEnabled(false); } -long +sv_frame_t ItemEditDialog::getFrameDuration() const { return m_frameDurationSpinBox->value(); @@ -264,7 +267,7 @@ float ItemEditDialog::getValue() const { - return m_valueSpinBox->value(); + return float(m_valueSpinBox->value()); } void @@ -303,8 +306,8 @@ { RealTime rt = getRealTime(); rt.sec = i; - size_t frame = RealTime::realTime2Frame(rt, m_sampleRate); - m_frameTimeSpinBox->setValue(frame); + sv_frame_t frame = RealTime::realTime2Frame(rt, m_sampleRate); + m_frameTimeSpinBox->setValue(int(frame)); m_resetButton->setEnabled(true); } @@ -313,8 +316,8 @@ { RealTime rt = getRealTime(); rt.nsec = i * 1000; - size_t frame = RealTime::realTime2Frame(rt, m_sampleRate); - m_frameTimeSpinBox->setValue(frame); + sv_frame_t frame = RealTime::realTime2Frame(rt, m_sampleRate); + m_frameTimeSpinBox->setValue(int(frame)); m_resetButton->setEnabled(true); } @@ -338,8 +341,8 @@ { RealTime rt = getRealDuration(); rt.sec = i; - size_t frame = RealTime::realTime2Frame(rt, m_sampleRate); - m_frameDurationSpinBox->setValue(frame); + sv_frame_t frame = RealTime::realTime2Frame(rt, m_sampleRate); + m_frameDurationSpinBox->setValue(int(frame)); m_resetButton->setEnabled(true); } @@ -348,8 +351,8 @@ { RealTime rt = getRealDuration(); rt.nsec = i * 1000; - size_t frame = RealTime::realTime2Frame(rt, m_sampleRate); - m_frameDurationSpinBox->setValue(frame); + sv_frame_t frame = RealTime::realTime2Frame(rt, m_sampleRate); + m_frameDurationSpinBox->setValue(int(frame)); m_resetButton->setEnabled(true); } diff -r 282f4be8f058 -r c02c51ae5238 widgets/ItemEditDialog.h --- a/widgets/ItemEditDialog.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/ItemEditDialog.h Wed Apr 20 12:06:28 2016 +0100 @@ -37,17 +37,17 @@ ShowText = 1 << 3 }; - ItemEditDialog(size_t sampleRate, int options, QString valueUnits = "", + ItemEditDialog(sv_samplerate_t sampleRate, int options, QString valueUnits = "", QWidget *parent = 0); - void setFrameTime(long frame); - long getFrameTime() const; + void setFrameTime(sv_frame_t frame); + sv_frame_t getFrameTime() const; void setRealTime(RealTime rt); RealTime getRealTime() const; - void setFrameDuration(long frame); - long getFrameDuration() const; + void setFrameDuration(sv_frame_t frame); + sv_frame_t getFrameDuration() const; void setRealDuration(RealTime rt); RealTime getRealDuration() const; @@ -59,10 +59,10 @@ QString getText() const; protected slots: - void frameTimeChanged(int); + void frameTimeChanged(int); // must be int as invoked from int signal void realTimeSecsChanged(int); void realTimeUSecsChanged(int); - void frameDurationChanged(int); + void frameDurationChanged(int); // must be int as invoked from int signal void realDurationSecsChanged(int); void realDurationUSecsChanged(int); void valueChanged(double); @@ -70,9 +70,9 @@ void reset(); protected: - size_t m_sampleRate; - long m_defaultFrame; - long m_defaultDuration; + sv_samplerate_t m_sampleRate; + sv_frame_t m_defaultFrame; + sv_frame_t m_defaultDuration; float m_defaultValue; QString m_defaultText; QSpinBox *m_frameTimeSpinBox; diff -r 282f4be8f058 -r c02c51ae5238 widgets/KeyReference.cpp --- a/widgets/KeyReference.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/KeyReference.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -24,6 +24,7 @@ #include KeyReference::KeyReference() : + m_text(0), m_dialog(0) { } @@ -49,7 +50,7 @@ QString name = action->text(); if (overrideName != "") name = overrideName; - QString shortcut = action->shortcut().toString(); + QString shortcut = action->shortcut().toString(QKeySequence::NativeText); QString tip = action->statusTip(); registerShortcut(name, shortcut, tip); @@ -87,6 +88,13 @@ } void +KeyReference::registerAlternativeShortcut(QAction *action, QKeySequence shortcut) +{ + QString name = action->text(); + registerAlternativeShortcut(name, shortcut.toString(QKeySequence::NativeText)); +} + +void KeyReference::registerAlternativeShortcut(QString name, QString alternative) { name.replace(tr("&"), ""); @@ -102,6 +110,12 @@ } void +KeyReference::registerAlternativeShortcut(QString name, QKeySequence shortcut) +{ + registerAlternativeShortcut(name, shortcut.toString(QKeySequence::NativeText)); +} + +void KeyReference::show() { if (m_dialog) { @@ -147,7 +161,7 @@ altdesc = tr(" (%1)").arg(altdesc); } - text += QString(" %1%2 %3%4\n") + text += QString(" %1%2 %3%4\n") .arg(shortcut).arg(altdesc).arg(actionName).arg(tip); } } @@ -159,7 +173,8 @@ m_text->setReadOnly(true); m_dialog = new QDialog; - m_dialog->setWindowTitle(tr("Sonic Visualiser: Key and Mouse Reference")); + m_dialog->setWindowTitle(tr("%1: Key and Mouse Reference") + .arg(QApplication::applicationName())); QVBoxLayout *layout = new QVBoxLayout; m_dialog->setLayout(layout); diff -r 282f4be8f058 -r c02c51ae5238 widgets/KeyReference.h --- a/widgets/KeyReference.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/KeyReference.h Wed Apr 20 12:06:28 2016 +0100 @@ -20,6 +20,7 @@ #include #include #include +#include class QAction; class QTextEdit; @@ -38,9 +39,11 @@ void registerShortcut(QAction *, QString overrideName = ""); void registerAlternativeShortcut(QAction *, QString alternative); + void registerAlternativeShortcut(QAction *, QKeySequence alternative); void registerShortcut(QString actionName, QString shortcut, QString tipText); void registerAlternativeShortcut(QString actionName, QString alternative); + void registerAlternativeShortcut(QString actionName, QKeySequence alternative); void show(); void hide(); diff -r 282f4be8f058 -r c02c51ae5238 widgets/LEDButton.cpp --- a/widgets/LEDButton.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/LEDButton.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -38,8 +38,6 @@ int dark_factor; QColor offcolor; - QPixmap *off_map; - QPixmap *on_map; }; @@ -51,8 +49,6 @@ d = new LEDButton::LEDButtonPrivate; d->dark_factor = 300; d->offcolor = col.dark(300); - d->off_map = 0; - d->on_map = 0; setColor(col); } @@ -65,8 +61,6 @@ d = new LEDButton::LEDButtonPrivate; d->dark_factor = 300; d->offcolor = col.dark(300); - d->off_map = 0; - d->on_map = 0; setColor(col); } @@ -78,16 +72,12 @@ d = new LEDButton::LEDButtonPrivate; d->dark_factor = 300; d->offcolor = col.dark(300); - d->off_map = 0; - d->on_map = 0; setColor(col); } LEDButton::~LEDButton() { - delete d->off_map; - delete d->on_map; delete d; } @@ -135,40 +125,7 @@ if (width < 0) width = 0; - QPixmap *tmpMap = 0; - - if (led_state) { - if (d->on_map) { - if (d->on_map->size() == size()) { - paint.begin(this); - paint.drawPixmap(0, 0, *d->on_map); - paint.end(); - return; - } else { - delete d->on_map; - d->on_map = 0; - } - } - } else { - if (d->off_map) { - if (d->off_map->size() == size()) { - paint.begin(this); - paint.drawPixmap(0, 0, *d->off_map); - paint.end(); - return; - } else { - delete d->off_map; - d->off_map = 0; - } - } - } - - int scale = 1; - width *= scale; - - tmpMap = new QPixmap(width, width); - tmpMap->fill(palette().background().color()); - paint.begin(tmpMap); + paint.begin(this); paint.setRenderHint(QPainter::Antialiasing, true); @@ -182,14 +139,14 @@ paint.setBrush(brush); // Draws a "flat" LED with the given color: - paint.drawEllipse( scale, scale, width - scale*2, width - scale*2 ); + paint.drawEllipse( 1, 1, width - 2, width - 2 ); // Draw the bright light spot of the LED now, using modified "old" // painter routine taken from KDEUI´s LEDButton widget: // Setting the new width of the pen is essential to avoid "pixelized" // shadow like it can be observed with the old LED code - pen.setWidth( 2 * scale ); + pen.setWidth( 2 ); // shrink the light on the LED to a size about 2/3 of the complete LED int pos = width/5 + 1; @@ -217,12 +174,13 @@ pos++; light_width--; } + paint.drawPoint(pos, pos); + // Drawing of bright spot finished, now draw a thin border // around the LED which resembles a shadow with light coming // from the upper left. -// pen.setWidth( 2 * scale + 1 ); // ### shouldn't this value be smaller for smaller LEDs? - pen.setWidth(2 * scale); + pen.setWidth(2); brush.setStyle(Qt::NoBrush); paint.setBrush(brush); // This avoids filling of the ellipse @@ -235,36 +193,13 @@ for (int arc = 120; arc < 2880; arc += 240) { pen.setColor(color); paint.setPen(pen); - int w = width - pen.width()/2 - scale + 1; + int w = width - pen.width()/2; paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle + arc, 240); paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle - arc, 240); color = color.dark(110); //FIXME: this should somehow use the contrast value } // end for ( angle = 720; angle < 6480; angle += 160 ) paint.end(); - // - // painting done - - QPixmap *&dest = led_state ? d->on_map : d->off_map; - - if (scale > 1) { - - QImage i = tmpMap->toImage(); - width /= scale; - delete tmpMap; - dest = new QPixmap(QPixmap::fromImage - (i.scaled(width, width, - Qt::KeepAspectRatio, - Qt::SmoothTransformation))); - - } else { - - dest = tmpMap; - } - - paint.begin(this); - paint.drawPixmap(0, 0, *dest); - paint.end(); } bool @@ -303,10 +238,6 @@ if(led_color!=col) { led_color = col; d->offcolor = col.dark(d->dark_factor); - delete d->on_map; - d->on_map = 0; - delete d->off_map; - d->off_map = 0; update(); } } diff -r 282f4be8f058 -r c02c51ae5238 widgets/LayerTree.cpp --- a/widgets/LayerTree.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/LayerTree.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -155,12 +155,12 @@ } void -ModelMetadataModel::propertyContainerPropertyChanged(PropertyContainer *pc) +ModelMetadataModel::propertyContainerPropertyChanged(PropertyContainer *) { } void -ModelMetadataModel::playParametersAudibilityChanged(bool a) +ModelMetadataModel::playParametersAudibilityChanged(bool ) { } @@ -169,7 +169,7 @@ { if (!index.isValid()) return QVariant(); - QObject *obj = static_cast(index.internalPointer()); +// QObject *obj = static_cast(index.internalPointer()); int row = index.row(), col = index.column(); //!!! not exactly the ideal use of a std::set @@ -206,13 +206,13 @@ } bool -ModelMetadataModel::setData(const QModelIndex &index, const QVariant &value, int role) +ModelMetadataModel::setData(const QModelIndex &, const QVariant &, int ) { return false; } Qt::ItemFlags -ModelMetadataModel::flags(const QModelIndex &index) const +ModelMetadataModel::flags(const QModelIndex &) const { Qt::ItemFlags flags = Qt::ItemIsEnabled; return flags; @@ -237,7 +237,7 @@ ModelMetadataModel::index(int row, int column, const QModelIndex &parent) const { if (!parent.isValid()) { - if (row >= m_models.size()) return QModelIndex(); + if (row >= (int)m_models.size()) return QModelIndex(); return createIndex(row, column, (void *)0); } @@ -245,7 +245,7 @@ } QModelIndex -ModelMetadataModel::parent(const QModelIndex &index) const +ModelMetadataModel::parent(const QModelIndex &) const { return QModelIndex(); } @@ -253,12 +253,12 @@ int ModelMetadataModel::rowCount(const QModelIndex &parent) const { - if (!parent.isValid()) return m_models.size(); + if (!parent.isValid()) return int(m_models.size()); return 0; } int -ModelMetadataModel::columnCount(const QModelIndex &parent) const +ModelMetadataModel::columnCount(const QModelIndex &) const { return m_columnCount; } diff -r 282f4be8f058 -r c02c51ae5238 widgets/LevelPanToolButton.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/LevelPanToolButton.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,173 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "LevelPanToolButton.h" +#include "LevelPanWidget.h" + +#include +#include +#include +#include +#include + +#include +using std::cerr; +using std::endl; + +LevelPanToolButton::LevelPanToolButton(QWidget *parent) : + QToolButton(parent), + m_pixels(32), + m_pixelsBig(32 * 3), + m_muted(false), + m_savedLevel(1.f) +{ + m_lpw = new LevelPanWidget(); + + connect(m_lpw, SIGNAL(levelChanged(float)), this, SIGNAL(levelChanged(float))); + connect(m_lpw, SIGNAL(levelChanged(float)), this, SLOT(selfLevelChanged(float))); + + connect(m_lpw, SIGNAL(panChanged(float)), this, SIGNAL(panChanged(float))); + connect(m_lpw, SIGNAL(panChanged(float)), this, SLOT(update())); + + connect(this, SIGNAL(clicked(bool)), this, SLOT(selfClicked())); + + QMenu *menu = new QMenu(); + QWidgetAction *wa = new QWidgetAction(menu); + wa->setDefaultWidget(m_lpw); + menu->addAction(wa); + + setPopupMode(InstantPopup); + setMenu(menu); + + setImageSize(m_pixels); + setBigImageSize(m_pixelsBig); +} + +LevelPanToolButton::~LevelPanToolButton() +{ +} + +float +LevelPanToolButton::getLevel() const +{ + return m_lpw->getLevel(); +} + +float +LevelPanToolButton::getPan() const +{ + return m_lpw->getPan(); +} + +bool +LevelPanToolButton::includesMute() const +{ + return m_lpw->includesMute(); +} + +void +LevelPanToolButton::setImageSize(int pixels) +{ + m_pixels = pixels; + + QPixmap px(m_pixels, m_pixels); + px.fill(Qt::transparent); + setIcon(px); +} + +void +LevelPanToolButton::setBigImageSize(int pixels) +{ + m_pixelsBig = pixels; + + m_lpw->setFixedWidth(m_pixelsBig); + m_lpw->setFixedHeight(m_pixelsBig); +} + +void +LevelPanToolButton::setLevel(float level) +{ + m_lpw->setLevel(level); + update(); +} + +void +LevelPanToolButton::setPan(float pan) +{ + m_lpw->setPan(pan); + update(); +} + +void +LevelPanToolButton::setIncludeMute(bool include) +{ + m_lpw->setIncludeMute(include); + update(); +} + +void +LevelPanToolButton::setEnabled(bool enabled) +{ + m_lpw->setEnabled(enabled); + QToolButton::setEnabled(enabled); +} + +void +LevelPanToolButton::selfLevelChanged(float level) +{ + if (level > 0.f) { + m_muted = false; + } else { + m_muted = true; + m_savedLevel = 1.f; + } + update(); +} + +void +LevelPanToolButton::selfClicked() +{ + cerr << "selfClicked" << endl; + + if (m_muted) { + m_muted = false; + m_lpw->setLevel(m_savedLevel); + emit levelChanged(m_savedLevel); + } else { + m_savedLevel = m_lpw->getLevel(); + m_muted = true; + m_lpw->setLevel(0.f); + emit levelChanged(0.f); + } + update(); +} + +void +LevelPanToolButton::paintEvent(QPaintEvent *) +{ + QStylePainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + opt.features &= (~QStyleOptionToolButton::HasMenu); + p.drawComplexControl(QStyle::CC_ToolButton, opt); + + if (m_pixels >= height()) { + setImageSize(height()-1); + } + + double margin = (double(height()) - m_pixels) / 2.0; + m_lpw->renderTo(this, QRectF(margin, margin, m_pixels, m_pixels), false); +} + + diff -r 282f4be8f058 -r c02c51ae5238 widgets/LevelPanToolButton.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/LevelPanToolButton.h Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,73 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef LEVEL_PAN_TOOLBUTTON_H +#define LEVEL_PAN_TOOLBUTTON_H + +#include + +class LevelPanWidget; + +class LevelPanToolButton : public QToolButton +{ + Q_OBJECT + +public: + LevelPanToolButton(QWidget *parent = 0); + ~LevelPanToolButton(); + + /// Return level as a gain value in the range [0,1] + float getLevel() const; + + /// Return pan as a value in the range [-1,1] + float getPan() const; + + /// Discover whether the level range includes muting or not + bool includesMute() const; + + void setImageSize(int pixels); + + void setBigImageSize(int pixels); + +public slots: + /// Set level in the range [0,1] -- will be rounded + void setLevel(float); + + /// Set pan in the range [-1,1] -- will be rounded + void setPan(float); + + /// Specify whether the level range should include muting or not + void setIncludeMute(bool); + + void setEnabled(bool enabled); + +signals: + void levelChanged(float); + void panChanged(float); + +private slots: + void selfLevelChanged(float); + void selfClicked(); + +protected: + void paintEvent(QPaintEvent *); + + LevelPanWidget *m_lpw; + int m_pixels; + int m_pixelsBig; + bool m_muted; + float m_savedLevel; +}; + +#endif diff -r 282f4be8f058 -r c02c51ae5238 widgets/LevelPanWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/LevelPanWidget.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,388 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "LevelPanWidget.h" + +#include +#include +#include + +#include "layer/ColourMapper.h" +#include "base/AudioLevel.h" + +#include +#include +#include + +using std::cerr; +using std::endl; + +static const int maxLevel = 4; // min is 0, may be mute or not depending on m_includeMute +static const int maxPan = 2; // range is -maxPan to maxPan + +LevelPanWidget::LevelPanWidget(QWidget *parent) : + QWidget(parent), + m_level(maxLevel), + m_pan(0), + m_editable(true), + m_includeMute(true) +{ +} + +LevelPanWidget::~LevelPanWidget() +{ +} + +QSize +LevelPanWidget::sizeHint() const +{ + static double ratio = 0.0; + if (ratio == 0.0) { + double baseEm; +#ifdef Q_OS_MAC + baseEm = 17.0; +#else + baseEm = 15.0; +#endif + double em = QFontMetrics(QFont()).height(); + ratio = em / baseEm; + } + + int pixels = 40; + int scaled = int(pixels * ratio + 0.5); + if (pixels != 0 && scaled == 0) scaled = 1; + return QSize(scaled, scaled); +} + +static int +db_to_level(double db) +{ + // Only if !m_includeMute, otherwise AudioLevel is used. + // Levels are: +6 0 -6 -12 -20 + assert(maxLevel == 4); + if (db > 3.) return 4; + else if (db > -3.) return 3; + else if (db > -9.) return 2; + else if (db > -16.) return 1; + else return 0; +} + +static double +level_to_db(int level) +{ + // Only if !m_includeMute, otherwise AudioLevel is used. + // Levels are: +6 0 -6 -12 -20 + assert(maxLevel == 4); + if (level >= 4) return 6.; + else if (level == 3) return 0.; + else if (level == 2) return -6.; + else if (level == 1) return -12.; + else return -20.; +} + +void +LevelPanWidget::setLevel(float flevel) +{ + int level; + if (m_includeMute) { + level = AudioLevel::multiplier_to_fader + (flevel, maxLevel, AudioLevel::ShortFader); + } else { + level = db_to_level(AudioLevel::multiplier_to_dB(flevel)); + } + if (level < 0) level = 0; + if (level > maxLevel) level = maxLevel; + if (level != m_level) { + m_level = level; + float convertsTo = getLevel(); + if (fabsf(convertsTo - flevel) > 1e-5) { + emitLevelChanged(); + } + update(); + } +} + +float +LevelPanWidget::getLevel() const +{ + if (m_includeMute) { + return float(AudioLevel::fader_to_multiplier + (m_level, maxLevel, AudioLevel::ShortFader)); + } else { + return float(AudioLevel::dB_to_multiplier(level_to_db(m_level))); + } +} + +void +LevelPanWidget::setPan(float pan) +{ + m_pan = int(round(pan * maxPan)); + if (m_pan < -maxPan) m_pan = -maxPan; + if (m_pan > maxPan) m_pan = maxPan; + update(); +} + +bool +LevelPanWidget::isEditable() const +{ + return m_editable; +} + +bool +LevelPanWidget::includesMute() const +{ + return m_includeMute; +} + +void +LevelPanWidget::setEditable(bool editable) +{ + m_editable = editable; + update(); +} + +void +LevelPanWidget::setIncludeMute(bool include) +{ + m_includeMute = include; + emitLevelChanged(); + update(); +} + +float +LevelPanWidget::getPan() const +{ + return float(m_pan) / float(maxPan); +} + +void +LevelPanWidget::emitLevelChanged() +{ + cerr << "emitting levelChanged(" << getLevel() << ")" << endl; + emit levelChanged(getLevel()); +} + +void +LevelPanWidget::emitPanChanged() +{ + cerr << "emitting panChanged(" << getPan() << ")" << endl; + emit panChanged(getPan()); +} + +void +LevelPanWidget::mousePressEvent(QMouseEvent *e) +{ + mouseMoveEvent(e); +} + +void +LevelPanWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (!m_editable) return; + + int level, pan; + toCell(rect(), e->pos(), level, pan); + if (level == m_level && pan == m_pan) { + return; + } + if (level != m_level) { + m_level = level; + emitLevelChanged(); + } + if (pan != m_pan) { + m_pan = pan; + emitPanChanged(); + } + update(); +} + +void +LevelPanWidget::mouseReleaseEvent(QMouseEvent *e) +{ + mouseMoveEvent(e); +} + +void +LevelPanWidget::wheelEvent(QWheelEvent *e) +{ + if (e->modifiers() & Qt::ControlModifier) { + if (e->delta() > 0) { + if (m_pan < maxPan) { + ++m_pan; + emitPanChanged(); + update(); + } + } else { + if (m_pan > -maxPan) { + --m_pan; + emitPanChanged(); + update(); + } + } + } else { + if (e->delta() > 0) { + if (m_level < maxLevel) { + ++m_level; + emitLevelChanged(); + update(); + } + } else { + if (m_level > 0) { + --m_level; + emitLevelChanged(); + update(); + } + } + } +} + +void +LevelPanWidget::toCell(QRectF rect, QPointF loc, int &level, int &pan) const +{ + double w = rect.width(), h = rect.height(); + + int npan = maxPan * 2 + 1; + int nlevel = maxLevel + 1; + + double wcell = w / npan, hcell = h / nlevel; + + level = int((h - (loc.y() - rect.y())) / hcell); + if (level < 0) level = 0; + if (level > maxLevel) level = maxLevel; + + pan = int((loc.x() - rect.x()) / wcell) - maxPan; + if (pan < -maxPan) pan = -maxPan; + if (pan > maxPan) pan = maxPan; +} + +QSizeF +LevelPanWidget::cellSize(QRectF rect) const +{ + double w = rect.width(), h = rect.height(); + int npan = maxPan * 2 + 1; + int nlevel = maxLevel + 1; + double wcell = w / npan, hcell = h / nlevel; + return QSizeF(wcell, hcell); +} + +QPointF +LevelPanWidget::cellCentre(QRectF rect, int level, int pan) const +{ + QSizeF cs = cellSize(rect); + return QPointF(rect.x() + cs.width() * (pan + maxPan) + cs.width() / 2., + rect.y() + rect.height() - cs.height() * (level + 1) + cs.height() / 2.); +} + +QSizeF +LevelPanWidget::cellLightSize(QRectF rect) const +{ + double extent = 3. / 4.; + QSizeF cs = cellSize(rect); + double m = std::min(cs.width(), cs.height()); + return QSizeF(m * extent, m * extent); +} + +QRectF +LevelPanWidget::cellLightRect(QRectF rect, int level, int pan) const +{ + QSizeF cls = cellLightSize(rect); + QPointF cc = cellCentre(rect, level, pan); + return QRectF(cc.x() - cls.width() / 2., + cc.y() - cls.height() / 2., + cls.width(), + cls.height()); +} + +double +LevelPanWidget::thinLineWidth(QRectF rect) const +{ + double tw = ceil(rect.width() / (maxPan * 2. * 10.)); + double th = ceil(rect.height() / (maxLevel * 10.)); + return std::min(th, tw); +} + +static QColor +level_to_colour(int level) +{ + assert(maxLevel == 4); + if (level == 0) return Qt::black; + else if (level == 1) return QColor(80, 0, 0); + else if (level == 2) return QColor(160, 0, 0); + else if (level == 3) return QColor(255, 0, 0); + else return QColor(255, 255, 0); +} + +void +LevelPanWidget::renderTo(QPaintDevice *dev, QRectF rect, bool asIfEditable) const +{ + QPainter paint(dev); + + paint.setRenderHint(QPainter::Antialiasing, true); + + QPen pen; + + double thin = thinLineWidth(rect); + + pen.setColor(QColor(127, 127, 127, 127)); + pen.setWidthF(cellLightSize(rect).width() + thin); + pen.setCapStyle(Qt::RoundCap); + paint.setPen(pen); + + for (int pan = -maxPan; pan <= maxPan; ++pan) { + paint.drawLine(cellCentre(rect, 0, pan), cellCentre(rect, maxLevel, pan)); + } + + if (isEnabled()) { + pen.setColor(Qt::black); + } else { + pen.setColor(Qt::darkGray); + } + + if (!asIfEditable && m_includeMute && m_level == 0) { + pen.setWidthF(thin * 2); + pen.setCapStyle(Qt::RoundCap); + paint.setPen(pen); + paint.drawLine(cellCentre(rect, 0, -maxPan), + cellCentre(rect, maxLevel, maxPan)); + paint.drawLine(cellCentre(rect, maxLevel, -maxPan), + cellCentre(rect, 0, maxPan)); + return; + } + + pen.setWidthF(thin); + pen.setCapStyle(Qt::FlatCap); + paint.setPen(pen); + + for (int level = 0; level <= m_level; ++level) { + if (isEnabled()) { + paint.setBrush(level_to_colour(level)); + } + QRectF clr = cellLightRect(rect, level, m_pan); + if (m_includeMute && m_level == 0) { + paint.drawLine(clr.topLeft(), clr.bottomRight()); + paint.drawLine(clr.bottomLeft(), clr.topRight()); + } else { + paint.drawEllipse(clr); + } + } +} + +void +LevelPanWidget::paintEvent(QPaintEvent *) +{ + renderTo(this, rect(), m_editable); +} + + + diff -r 282f4be8f058 -r c02c51ae5238 widgets/LevelPanWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/LevelPanWidget.h Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,89 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef LEVEL_PAN_WIDGET_H +#define LEVEL_PAN_WIDGET_H + +#include + +/** + * A simple widget for coarse level and pan control. + */ + +class LevelPanWidget : public QWidget +{ + Q_OBJECT + +public: + LevelPanWidget(QWidget *parent = 0); + ~LevelPanWidget(); + + /// Return level as a gain value in the range [0,1] + float getLevel() const; + + /// Return pan as a value in the range [-1,1] + float getPan() const; + + /// Find out whether the widget is editable + bool isEditable() const; + + /// Discover whether the level range includes muting or not + bool includesMute() const; + + /// Draw a suitably sized copy of the widget's contents to the given device + void renderTo(QPaintDevice *, QRectF, bool asIfEditable) const; + + QSize sizeHint() const; + +public slots: + /// Set level in the range [0,1] -- will be rounded + void setLevel(float); + + /// Set pan in the range [-1,1] -- will be rounded + void setPan(float); + + /// Specify whether the widget is editable or read-only (default editable) + void setEditable(bool); + + /// Specify whether the level range should include muting or not + void setIncludeMute(bool); + +signals: + void levelChanged(float); + void panChanged(float); + +protected: + virtual void mousePressEvent(QMouseEvent *ev); + virtual void mouseMoveEvent(QMouseEvent *ev); + virtual void mouseReleaseEvent(QMouseEvent *ev); + virtual void wheelEvent(QWheelEvent *ev); + virtual void paintEvent(QPaintEvent *ev); + + void emitLevelChanged(); + void emitPanChanged(); + + int m_level; + int m_pan; + bool m_editable; + bool m_includeMute; + + QSizeF cellSize(QRectF) const; + QPointF cellCentre(QRectF, int level, int pan) const; + QSizeF cellLightSize(QRectF) const; + QRectF cellLightRect(QRectF, int level, int pan) const; + double thinLineWidth(QRectF) const; + void toCell(QRectF, QPointF loc, int &level, int &pan) const; +}; + +#endif diff -r 282f4be8f058 -r c02c51ae5238 widgets/ListInputDialog.cpp --- a/widgets/ListInputDialog.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/ListInputDialog.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -67,7 +67,7 @@ { for (size_t i = 0; i < m_radioButtons.size(); ++i) { if (m_radioButtons[i]->isChecked()) { - return m_strings[i]; + return m_strings[int(i)]; } } return ""; diff -r 282f4be8f058 -r c02c51ae5238 widgets/ModelDataTableDialog.cpp --- a/widgets/ModelDataTableDialog.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/ModelDataTableDialog.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -167,14 +167,14 @@ } void -ModelDataTableDialog::userScrolledToFrame(unsigned long frame) +ModelDataTableDialog::userScrolledToFrame(sv_frame_t frame) { QModelIndex index = m_table->getModelIndexForFrame(frame); makeCurrent(index.row()); } void -ModelDataTableDialog::playbackScrolledToFrame(unsigned long frame) +ModelDataTableDialog::playbackScrolledToFrame(sv_frame_t frame) { if (m_trackPlayback) { QModelIndex index = m_table->getModelIndexForFrame(frame); @@ -247,14 +247,14 @@ } void -ModelDataTableDialog::viewPressed(const QModelIndex &index) +ModelDataTableDialog::viewPressed(const QModelIndex &) { // SVDEBUG << "ModelDataTableDialog::viewPressed: " << index.row() << ", " << index.column() << endl; } void ModelDataTableDialog::currentChanged(const QModelIndex ¤t, - const QModelIndex &previous) + const QModelIndex &) { // SVDEBUG << "ModelDataTableDialog::currentChanged: from " // << previous.row() << ", " << previous.column() diff -r 282f4be8f058 -r c02c51ae5238 widgets/ModelDataTableDialog.h --- a/widgets/ModelDataTableDialog.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/ModelDataTableDialog.h Wed Apr 20 12:06:28 2016 +0100 @@ -18,6 +18,8 @@ #include +#include "base/BaseTypes.h" + class TabularModel; class ModelDataTableModel; class QTableView; @@ -38,11 +40,11 @@ QToolBar *getPlayToolbar() { return m_playToolbar; } signals: - void scrollToFrame(unsigned long frame); + void scrollToFrame(sv_frame_t frame); public slots: - void userScrolledToFrame(unsigned long frame); - void playbackScrolledToFrame(unsigned long frame); + void userScrolledToFrame(sv_frame_t frame); + void playbackScrolledToFrame(sv_frame_t frame); void addCommand(Command *); protected slots: diff -r 282f4be8f058 -r c02c51ae5238 widgets/Panner.cpp --- a/widgets/Panner.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/Panner.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -36,7 +36,9 @@ m_thumbColour(palette().highlightedText().color()), m_backgroundAlpha(255), m_thumbAlpha(255), - m_clicked(false) + m_clicked(false), + m_dragStartX(0), + m_dragStartY(0) { } @@ -63,7 +65,7 @@ float unit = m_scrollUnit; if (unit == 0.f) { unit = float(m_rectHeight) / (6 * float(height())); - if (unit < 0.01) unit = 0.01; + if (unit < 0.01f) unit = 0.01f; } if (!up) { @@ -163,24 +165,24 @@ paint.setBrush(hl); - int rw = lrintf((width() - 1) * m_rectWidth); - int rh = lrintf((height() - 1) * m_rectHeight); + int rw = int(lrintf(float(width() - 1) * m_rectWidth)); + int rh = int(lrintf(float(height() - 1) * m_rectHeight)); if (rw < 2) rw = 2; if (rh < 2) rh = 2; - paint.drawRect(lrintf(width() * m_rectX), - lrintf(height() * m_rectY), + paint.drawRect(int(lrintf(float(width()) * m_rectX)), + int(lrintf(float(height()) * m_rectY)), rw, rh); } void Panner::normalise() { - if (m_rectWidth > 1.0) m_rectWidth = 1.0; - if (m_rectHeight > 1.0) m_rectHeight = 1.0; - if (m_rectX + m_rectWidth > 1.0) m_rectX = 1.0 - m_rectWidth; + if (m_rectWidth > 1.f) m_rectWidth = 1.f; + if (m_rectHeight > 1.f) m_rectHeight = 1.f; + if (m_rectX + m_rectWidth > 1.f) m_rectX = 1.f - m_rectWidth; if (m_rectX < 0) m_rectX = 0; - if (m_rectY + m_rectHeight > 1.0) m_rectY = 1.0 - m_rectHeight; + if (m_rectY + m_rectHeight > 1.f) m_rectY = 1.f - m_rectHeight; if (m_rectY < 0) m_rectY = 0; if (!m_defaultsSet) { diff -r 282f4be8f058 -r c02c51ae5238 widgets/PluginParameterBox.cpp --- a/widgets/PluginParameterBox.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/PluginParameterBox.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -70,12 +70,12 @@ m_programCombo = new QComboBox; m_programCombo->setMaxVisibleItems - (m_programs.size() < 25 ? m_programs.size() : 20); + (int(m_programs.size() < 25 ? m_programs.size() : 20)); - for (size_t i = 0; i < m_programs.size(); ++i) { + for (int i = 0; in_range_for(m_programs, i); ++i) { m_programCombo->addItem(m_programs[i].c_str()); if (m_programs[i] == currentProgram) { - m_programCombo->setCurrentIndex(i); + m_programCombo->setCurrentIndex(int(i)); } } @@ -88,7 +88,7 @@ offset = 1; } - for (size_t i = 0; i < params.size(); ++i) { + for (int i = 0; in_range_for(params, i); ++i) { QString identifier = params[i].identifier.c_str(); QString name = params[i].name.c_str(); @@ -120,9 +120,9 @@ if (!(hint & PortHint::Logarithmic)) { if (qtz > 0.0) { - imax = lrintf((max - min) / qtz); + imax = int(lrintf((max - min) / qtz)); } else { - qtz = (max - min) / 100.0; + qtz = (max - min) / 100.f; } } @@ -131,7 +131,9 @@ QLabel *label = new QLabel(name); if (params[i].description != "") { - label->setToolTip(params[i].description.c_str()); + label->setToolTip(QString("%1") + .arg(params[i].description.c_str()) + .replace("\n", "
")); } m_layout->addWidget(label, i + offset, 0); @@ -243,19 +245,19 @@ if (ad && ad->rangeMapper()) { - newValue = ad->mappedValue(); + newValue = float(ad->mappedValue()); if (newValue < min) newValue = min; if (newValue > max) newValue = max; if (qtz != 0.0) { - ival = lrintf((newValue - min) / qtz); - newValue = min + ival * qtz; + ival = int(lrintf((newValue - min) / qtz)); + newValue = min + float(ival) * qtz; } } else { - if (qtz == 0.0) { - qtz = (max - min) / 100.0; + if (qtz == 0.f) { + qtz = (max - min) / 100.f; } - newValue = min + ival * qtz; + newValue = min + float(ival) * qtz; } // SVDEBUG << "PluginParameterBox::dialChanged: newValue = " << newValue << endl; @@ -327,19 +329,19 @@ if (params.isQuantized) qtz = params.quantizeStep; if (qtz > 0.0) { - int step = lrintf((value - min) / qtz); - value = min + step * qtz; + int step = int(lrintf(float(value - min) / qtz)); + value = min + float(step) * qtz; } - int imax = 100; +// int imax = 100; if (qtz > 0.0) { - imax = lrintf((max - min) / qtz); +// imax = lrintf((max - min) / qtz); } else { - qtz = (max - min) / 100.0; + qtz = (max - min) / 100.f; } - int ival = lrintf((value - min) / qtz); + int ival = int(lrintf(float(value - min) / qtz)); AudioDial *dial = m_params[identifier].dial; if (dial) { @@ -354,7 +356,7 @@ SVDEBUG << "setting plugin parameter \"" << identifier << "\" to value " << value << endl; - m_plugin->setParameter(identifier.toStdString(), value); + m_plugin->setParameter(identifier.toStdString(), float(value)); updateProgramCombo(); @@ -387,17 +389,17 @@ if (param.isQuantized) qtz = param.quantizeStep; if (qtz == 0.0) { - qtz = (max - min) / 100.0; + qtz = (max - min) / 100.f; } i->second.dial->blockSignals(true); - i->second.dial->setValue(lrintf((value - min) / qtz)); + i->second.dial->setValue(int(lrintf(float(value - min) / qtz))); i->second.dial->blockSignals(false); } if (i->second.combo) { i->second.combo->blockSignals(true); - i->second.combo->setCurrentIndex(lrintf(value)); + i->second.combo->setCurrentIndex(int(lrintf(value))); i->second.combo->blockSignals(false); } @@ -418,7 +420,7 @@ std::string currentProgram = m_plugin->getCurrentProgram(); - for (size_t i = 0; i < m_programs.size(); ++i) { + for (int i = 0; in_range_for(m_programs, i); ++i) { if (m_programs[i] == currentProgram) { m_programCombo->setCurrentIndex(i); } diff -r 282f4be8f058 -r c02c51ae5238 widgets/PluginParameterDialog.cpp --- a/widgets/PluginParameterDialog.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/PluginParameterDialog.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -51,7 +51,7 @@ m_blockSize(0), m_windowType(HanningWindow), m_parameterBox(0), - m_selectionOnly(false) + m_currentSelectionOnly(false) { setWindowTitle(tr("Plugin Parameters")); @@ -386,10 +386,10 @@ int size = 1024; int increment = 1024; if (fePlugin) { - size = fePlugin->getPreferredBlockSize(); + size = int(fePlugin->getPreferredBlockSize()); cerr << "Feature extraction plugin \"" << fePlugin->getName() << "\" reports preferred block size as " << size << endl; if (size == 0) size = 1024; - increment = fePlugin->getPreferredStepSize(); + increment = int(fePlugin->getPreferredStepSize()); if (increment == 0) { if (fePlugin->getInputDomain() == Vamp::Plugin::TimeDomain) { increment = size; @@ -540,15 +540,15 @@ } void -PluginParameterDialog::getProcessingParameters(size_t &blockSize) const +PluginParameterDialog::getProcessingParameters(int &blockSize) const { blockSize = m_blockSize; return; } void -PluginParameterDialog::getProcessingParameters(size_t &stepSize, - size_t &blockSize, +PluginParameterDialog::getProcessingParameters(int &stepSize, + int &blockSize, WindowType &windowType) const { stepSize = m_stepSize; diff -r 282f4be8f058 -r c02c51ae5238 widgets/PluginParameterDialog.h --- a/widgets/PluginParameterDialog.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/PluginParameterDialog.h Wed Apr 20 12:06:28 2016 +0100 @@ -70,8 +70,8 @@ //!!! merge with PluginTransform::ExecutionContext - void getProcessingParameters(size_t &blockSize) const; - void getProcessingParameters(size_t &stepSize, size_t &blockSize, + void getProcessingParameters(int &blockSize) const; + void getProcessingParameters(int &stepSize, int &blockSize, WindowType &windowType) const; int exec(); @@ -96,8 +96,8 @@ Vamp::PluginBase *m_plugin; int m_channel; - size_t m_stepSize; - size_t m_blockSize; + int m_stepSize; + int m_blockSize; WindowType m_windowType; PluginParameterBox *m_parameterBox; diff -r 282f4be8f058 -r c02c51ae5238 widgets/PropertyBox.cpp --- a/widgets/PropertyBox.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/PropertyBox.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -24,10 +24,6 @@ #include "base/UnitDatabase.h" #include "base/RangeMapper.h" -#include "plugin/RealTimePluginFactory.h" -#include "plugin/RealTimePluginInstance.h" -#include "plugin/PluginXml.h" - #include "AudioDial.h" #include "LEDButton.h" #include "IconLoader.h" @@ -46,6 +42,7 @@ #include #include #include +#include #include #include @@ -71,6 +68,12 @@ m_mainBox = new QVBoxLayout; setLayout(m_mainBox); +#ifdef Q_OS_MAC + QMargins mm = m_mainBox->contentsMargins(); + QMargins mmhalf(mm.left()/2, mm.top()/3, mm.right()/2, mm.bottom()/3); + m_mainBox->setContentsMargins(mmhalf); +#endif + // m_nameWidget = new QLabel; // m_mainBox->addWidget(m_nameWidget); // m_nameWidget->setText(container->objectName()); @@ -84,6 +87,8 @@ m_layout = new QGridLayout; m_layout->setMargin(0); + m_layout->setHorizontalSpacing(2); + m_layout->setVerticalSpacing(1); m_mainWidget->setLayout(m_layout); PropertyContainer::PropertyList properties = m_container->getProperties(); @@ -123,7 +128,7 @@ PropertyBox::populateViewPlayFrame() { #ifdef DEBUG_PROPERTY_BOX - cerr << "PropertyBox(" << m_container << ")::populateViewPlayFrame" << endl; + cerr << "PropertyBox[" << this << ":" << m_container << "]::populateViewPlayFrame" << endl; #endif if (m_viewPlayFrame) { @@ -194,13 +199,14 @@ layout->insertStretch(-1, 10); - if (params->getPlayPluginId() != "") { - QPushButton *pluginButton = new QPushButton(QIcon(":icons/faders.png"), ""); - pluginButton->setFixedWidth(24); - pluginButton->setFixedHeight(24); - layout->addWidget(pluginButton); - connect(pluginButton, SIGNAL(clicked()), - this, SLOT(editPlugin())); + if (params->getPlayClipId() != "") { + QPushButton *playParamButton = + new QPushButton(QIcon(":icons/faders.png"), ""); + playParamButton->setFixedWidth(24); + playParamButton->setFixedHeight(24); + layout->addWidget(playParamButton); + connect(playParamButton, SIGNAL(clicked()), + this, SLOT(editPlayParameters())); } AudioDial *gainDial = new AudioDial; @@ -506,7 +512,16 @@ if (type == PropertyContainer::ValueProperty) { for (int i = min; i <= max; ++i) { - cb->addItem(m_container->getPropertyValueLabel(name, i)); + + QString label = m_container->getPropertyValueLabel(name, i); + QString iname = m_container->getPropertyValueIconName(name, i); + + if (iname != "") { + QIcon icon(IconLoader().load(iname)); + cb->addItem(icon, label); + } else { + cb->addItem(label); + } } } else if (type == PropertyContainer::UnitsProperty) { @@ -524,7 +539,7 @@ // manages its own Add New Colour entry... ColourDatabase *db = ColourDatabase::getInstance(); - for (size_t i = 0; i < db->getColourCount(); ++i) { + for (int i = 0; i < db->getColourCount(); ++i) { QString name = db->getColourName(i); cb->addItem(db->getExamplePixmap(i, QSize(12, 12)), name); } @@ -575,13 +590,16 @@ cb->blockSignals(false); #ifdef Q_OS_MAC - // Crashes on startup without this, for some reason - cb->setMinimumSize(QSize(10, 10)); + // Crashes on startup without this, for some reason; also + // prevents combo boxes from getting weirdly squished + // vertically + cb->setMinimumSize(QSize(10, cb->font().pixelSize() * 2)); #endif break; } + case PropertyContainer::InvalidProperty: default: break; } @@ -624,7 +642,9 @@ void PropertyBox::unitDatabaseChanged() { +#ifdef DEBUG_PROPERTY_BOX cerr << "PropertyBox[" << this << "]: unitDatabaseChanged" << endl; +#endif blockSignals(true); // cerr << "my container is " << m_container << endl; @@ -749,7 +769,7 @@ void PropertyBox::playGainChanged(float gain) { - int dialValue = lrint(log10(gain) * 20.0); + int dialValue = int(lrint(log10(gain) * 20.0)); if (dialValue < -50) dialValue = -50; if (dialValue > 50) dialValue = 50; emit changePlayGainDial(dialValue); @@ -763,7 +783,7 @@ PlayParameters *params = m_container->getPlayParameters(); if (!params) return; - float gain = pow(10, float(dialValue) / 20.0); + float gain = float(pow(10, float(dialValue) / 20.0)); if (params->getPlayGain() != gain) { PlayParameterRepository::EditCommand *command = @@ -778,7 +798,7 @@ void PropertyBox::playPanChanged(float pan) { - int dialValue = lrint(pan * 50.0); + int dialValue = int(lrint(pan * 50.0)); if (dialValue < -50) dialValue = -50; if (dialValue > 50) dialValue = 50; emit changePlayPanDial(dialValue); @@ -792,9 +812,9 @@ PlayParameters *params = m_container->getPlayParameters(); if (!params) return; - float pan = float(dialValue) / 50.0; - if (pan < -1.0) pan = -1.0; - if (pan > 1.0) pan = 1.0; + float pan = float(dialValue) / 50.f; + if (pan < -1.f) pan = -1.f; + if (pan > 1.f) pan = 1.f; if (params->getPlayPan() != pan) { PlayParameterRepository::EditCommand *command = @@ -807,55 +827,62 @@ } void -PropertyBox::editPlugin() +PropertyBox::editPlayParameters() { - //!!! should probably just emit and let something else do this - PlayParameters *params = m_container->getPlayParameters(); if (!params) return; - QString pluginId = params->getPlayPluginId(); - QString configurationXml = params->getPlayPluginConfiguration(); + QString clip = params->getPlayClipId(); PlayParameterRepository::EditCommand *command = new PlayParameterRepository::EditCommand(params); - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - if (!factory) return; + QInputDialog *dialog = new QInputDialog(this); - RealTimePluginInstance *instance = - factory->instantiatePlugin(pluginId, 0, 0, 48000, 1024, 1); - if (!instance) return; + QDir dir(":/samples"); + QStringList clipFiles = dir.entryList(QStringList() << "*.wav", QDir::Files); - PluginXml(instance).setParametersFromXml(configurationXml); + QStringList clips; + foreach (QString str, clipFiles) { + clips.push_back(str.replace(".wav", "")); + } + dialog->setComboBoxItems(clips); - PluginParameterDialog *dialog = new PluginParameterDialog(instance); - connect(dialog, SIGNAL(pluginConfigurationChanged(QString)), - this, SLOT(pluginConfigurationChanged(QString))); + dialog->setLabelText(tr("Set playback clip:")); + + QComboBox *cb = dialog->findChild(); + if (cb) { + for (int i = 0; i < cb->count(); ++i) { + if (cb->itemText(i) == clip) { + cb->setCurrentIndex(i); + } + } + } + + connect(dialog, SIGNAL(textValueChanged(QString)), + this, SLOT(playClipChanged(QString))); if (dialog->exec() == QDialog::Accepted) { - QString newConfiguration = PluginXml(instance).toXmlString(); - command->setPlayPluginConfiguration(newConfiguration); + QString newClip = dialog->textValue(); + command->setPlayClipId(newClip); CommandHistory::getInstance()->addCommand(command, true); } else { delete command; // restore in case we mucked about with the configuration // as a consequence of signals from the dialog - params->setPlayPluginConfiguration(configurationXml); + params->setPlayClipId(clip); } delete dialog; - delete instance; } void -PropertyBox::pluginConfigurationChanged(QString configurationXml) +PropertyBox::playClipChanged(QString id) { PlayParameters *params = m_container->getPlayParameters(); if (!params) return; - params->setPlayPluginConfiguration(configurationXml); + params->setPlayClipId(id); } void @@ -885,7 +912,7 @@ QString extraText; AudioDial *dial = dynamic_cast(w); if (dial) { - float mv = dial->mappedValue(); + double mv = dial->mappedValue(); QString unit = ""; if (dial->rangeMapper()) unit = dial->rangeMapper()->getUnit(); if (unit != "") { diff -r 282f4be8f058 -r c02c51ae5238 widgets/PropertyBox.h --- a/widgets/PropertyBox.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/PropertyBox.h Wed Apr 20 12:06:28 2016 +0100 @@ -47,7 +47,7 @@ public slots: void propertyContainerPropertyChanged(PropertyContainer *); void propertyContainerPropertyRangeChanged(PropertyContainer *); - void pluginConfigurationChanged(QString); + void playClipChanged(QString); void layerVisibilityChanged(bool); protected slots: @@ -66,7 +66,7 @@ void unitDatabaseChanged(); void colourDatabaseChanged(); - void editPlugin(); + void editPlayParameters(); void mouseEnteredWidget(); void mouseLeftWidget(); diff -r 282f4be8f058 -r c02c51ae5238 widgets/PropertyStack.cpp --- a/widgets/PropertyStack.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/PropertyStack.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -23,6 +23,7 @@ #include "widgets/IconLoader.h" #include "base/Command.h" #include "widgets/CommandHistory.h" +#include "layer/ShowLayerCommand.h" #include #include @@ -49,7 +50,7 @@ tabBar()->setUsesScrollButtons(true); tabBar()->setIconSize(QSize(16, 16)); #endif - + repopulate(); connect(this, SIGNAL(currentChanged(int)), @@ -74,13 +75,17 @@ m_client, SLOT(propertyContainerSelected(View *, PropertyContainer *))); } +PropertyStack::~PropertyStack() +{ +} + void PropertyStack::repopulate() { blockSignals(true); #ifdef DEBUG_PROPERTY_STACK - SVDEBUG << "PropertyStack::repopulate" << endl; + cerr << "PropertyStack[" << this << "]::repopulate" << endl; #endif while (count() > 0) { @@ -91,11 +96,17 @@ } m_boxes.clear(); - for (size_t i = 0; i < m_client->getPropertyContainerCount(); ++i) { + for (int i = 0; i < m_client->getPropertyContainerCount(); ++i) { PropertyContainer *container = m_client->getPropertyContainer(i); QString name = container->getPropertyContainerName(); +#ifdef DEBUG_PROPERTY_STACK + cerr << "PropertyStack[" << this << "]::repopulate: client " << m_client + << " returns container " << container << " (name " << name + << ") at position " << i << endl; +#endif + PropertyBox *box = new PropertyBox(container); connect(box, SIGNAL(showLayer(bool)), this, SLOT(showLayer(bool))); @@ -142,7 +153,7 @@ bool PropertyStack::containsContainer(PropertyContainer *pc) const { - for (size_t i = 0; i < m_client->getPropertyContainerCount(); ++i) { + for (int i = 0; i < m_client->getPropertyContainerCount(); ++i) { PropertyContainer *container = m_client->getPropertyContainer(i); if (pc == container) return true; } @@ -153,9 +164,19 @@ int PropertyStack::getContainerIndex(PropertyContainer *pc) const { - for (size_t i = 0; i < m_client->getPropertyContainerCount(); ++i) { - PropertyContainer *container = m_client->getPropertyContainer(i); - if (pc == container) return i; + // This is used to obtain an index to be passed to setCurrentIndex + // -- which is the index of the property container's box in our + // stack of boxes. That is not the same thing as the index of the + // container (i.e. the layer) in the view: the view reorders its + // containers whenever one is raised to the top, while our boxes + // remain in the same order. So we must find this container in the + // box list, not in the view. + + for (int i = 0; in_range_for(m_boxes, i); ++i) { + PropertyContainer *container = m_boxes[i]->getContainer(); + if (pc == container) { + return i; + } } return false; @@ -207,27 +228,6 @@ repopulate(); } -class ShowLayerCommand : public QObject, public Command -{ -public: - ShowLayerCommand(View *view, Layer *layer, bool show, QString name) : - m_view(view), m_layer(layer), m_show(show), m_name(name) { } - void execute() { - m_layer->showLayer(m_view, m_show); - } - void unexecute() { - m_layer->showLayer(m_view, !m_show); - } - QString getName() const { - return m_name; - } -protected: - View *m_view; - Layer *m_layer; - bool m_show; - QString m_name; -}; - void PropertyStack::showLayer(bool show) { diff -r 282f4be8f058 -r c02c51ae5238 widgets/PropertyStack.h --- a/widgets/PropertyStack.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/PropertyStack.h Wed Apr 20 12:06:28 2016 +0100 @@ -31,6 +31,7 @@ public: PropertyStack(QWidget *parent, View *client); + virtual ~PropertyStack(); View *getClient() { return m_client; } bool containsContainer(PropertyContainer *container) const; diff -r 282f4be8f058 -r c02c51ae5238 widgets/SubdividingMenu.cpp --- a/widgets/SubdividingMenu.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/SubdividingMenu.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -22,7 +22,7 @@ using std::set; using std::map; -SubdividingMenu::SubdividingMenu(size_t lowerLimit, size_t upperLimit, +SubdividingMenu::SubdividingMenu(int lowerLimit, int upperLimit, QWidget *parent) : QMenu(parent), m_lowerLimit(lowerLimit ? lowerLimit : 14), @@ -31,8 +31,8 @@ { } -SubdividingMenu::SubdividingMenu(const QString &title, size_t lowerLimit, - size_t upperLimit, QWidget *parent) : +SubdividingMenu::SubdividingMenu(const QString &title, int lowerLimit, + int upperLimit, QWidget *parent) : QMenu(title, parent), m_lowerLimit(lowerLimit ? lowerLimit : 14), m_upperLimit(upperLimit ? upperLimit : (m_lowerLimit * 5) / 2), @@ -53,11 +53,11 @@ { m_entriesSet = true; - size_t total = entries.size(); + int total = int(entries.size()); if (total < m_upperLimit) return; - size_t count = 0; + int count = 0; QMenu *chunkMenu = new QMenu(); chunkMenu->setTearOffEnabled(isTearOffEnabled()); diff -r 282f4be8f058 -r c02c51ae5238 widgets/SubdividingMenu.h --- a/widgets/SubdividingMenu.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/SubdividingMenu.h Wed Apr 20 12:06:28 2016 +0100 @@ -39,10 +39,10 @@ Q_OBJECT public: - SubdividingMenu(size_t lowerLimit = 0, size_t upperLimit = 0, + SubdividingMenu(int lowerLimit = 0, int upperLimit = 0, QWidget *parent = 0); - SubdividingMenu(const QString &title, size_t lowerLimit = 0, - size_t upperLimit = 0, QWidget *parent = 0); + SubdividingMenu(const QString &title, int lowerLimit = 0, + int upperLimit = 0, QWidget *parent = 0); virtual ~SubdividingMenu(); void setEntries(const std::set &entries); @@ -65,8 +65,8 @@ protected: std::map m_nameToChunkMenuMap; - size_t m_lowerLimit; - size_t m_upperLimit; + int m_lowerLimit; + int m_upperLimit; bool m_entriesSet; std::map m_pendingEntries; diff -r 282f4be8f058 -r c02c51ae5238 widgets/Thumbwheel.cpp --- a/widgets/Thumbwheel.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/Thumbwheel.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -135,7 +135,7 @@ } void -Thumbwheel::setMappedValue(float mappedValue) +Thumbwheel::setMappedValue(double mappedValue) { if (m_rangeMapper) { int newValue = m_rangeMapper->getPositionForValue(mappedValue); @@ -202,7 +202,7 @@ return m_value; } -float +double Thumbwheel::getMappedValue() const { if (m_rangeMapper) { @@ -240,7 +240,7 @@ void Thumbwheel::scroll(bool up) { - int step = lrintf(m_speed); + int step = int(lrintf(m_speed)); if (step == 0) step = 1; if (up) { @@ -327,11 +327,11 @@ if (m_rangeMapper) { - float min = m_rangeMapper->getValueForPosition(m_min); - float max = m_rangeMapper->getValueForPosition(m_max); + double min = m_rangeMapper->getValueForPosition(m_min); + double max = m_rangeMapper->getValueForPosition(m_max); if (min > max) { - float tmp = min; + double tmp = min; min = max; max = tmp; } @@ -357,7 +357,7 @@ } } - float newValue = QInputDialog::getDouble + double newValue = QInputDialog::getDouble (this, tr("Enter new value"), text, @@ -398,10 +398,10 @@ dist = e->y() - m_clickPos.y(); } - float rotation = m_clickRotation + (m_speed * dist) / 100; + float rotation = m_clickRotation + (m_speed * float(dist)) / 100; if (rotation < 0.f) rotation = 0.f; if (rotation > 1.f) rotation = 1.f; - int value = lrintf(m_min + (m_max - m_min) * m_rotation); + int value = int(lrintf(float(m_min) + float(m_max - m_min) * m_rotation)); if (value != m_value) { setValue(value); if (m_tracking) emit valueChanged(getValue()); @@ -426,7 +426,7 @@ void Thumbwheel::wheelEvent(QWheelEvent *e) { - int step = lrintf(m_speed); + int step = int(lrintf(m_speed)); if (step == 0) step = 1; if (e->delta() > 0) { @@ -469,13 +469,13 @@ paint.setRenderHint(QPainter::Antialiasing, true); - float w = width(); - float w0 = 0.5; - float w1 = w - 0.5; + double w = width(); + double w0 = 0.5; + double w1 = w - 0.5; - float h = height(); - float h0 = 0.5; - float h1 = h - 0.5; + double h = height(); + double h0 = 0.5; + double h1 = h - 0.5; for (int i = bw-1; i >= 0; --i) { @@ -504,7 +504,7 @@ paint.setClipRect(subclip); - float radians = m_rotation * 1.5f * M_PI; + double radians = m_rotation * 1.5f * M_PI; // cerr << "value = " << m_value << ", min = " << m_min << ", max = " << m_max << ", rotation = " << rotation << endl; @@ -518,16 +518,16 @@ for (int i = 0; i < notches; ++i) { - float a0 = (2.f * M_PI * i) / notches + radians; - float a1 = a0 + M_PI / (notches * 2); - float a2 = (2.f * M_PI * (i + 1)) / notches + radians; + double a0 = (2.0 * M_PI * i) / notches + radians; + double a1 = a0 + M_PI / (notches * 2); + double a2 = (2.0 * M_PI * (i + 1)) / notches + radians; - float depth = cosf((a0 + a2) / 2); + double depth = cos((a0 + a2) / 2); if (depth < 0) continue; - float x0 = radius * sinf(a0) + w/2; - float x1 = radius * sinf(a1) + w/2; - float x2 = radius * sinf(a2) + w/2; + double x0 = radius * sin(a0) + w/2; + double x1 = radius * sin(a1) + w/2; + double x2 = radius * sin(a2) + w/2; if (x2 < 0 || x0 > w) continue; if (x0 < 0) x0 = 0; @@ -537,7 +537,7 @@ x1 += bw; x2 += bw; - int grey = lrintf(120 * depth); + int grey = int(lrint(120 * depth)); QColor fc = QColor(grey, grey, grey); QColor oc = palette().highlight().color(); @@ -548,9 +548,9 @@ paint.setBrush(oc); - float prop; + double prop; if (i >= notches / 4) { - prop = float(notches - (((i - float(notches) / 4.f) * 4.f) / 3.f)) + prop = double(notches - (((i - double(notches) / 4.f) * 4.f) / 3.f)) / notches; } else { prop = 0.f; diff -r 282f4be8f058 -r c02c51ae5238 widgets/Thumbwheel.h --- a/widgets/Thumbwheel.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/Thumbwheel.h Wed Apr 20 12:06:28 2016 +0100 @@ -41,7 +41,7 @@ void setRangeMapper(RangeMapper *mapper); // I take ownership, will delete const RangeMapper *getRangeMapper() const { return m_rangeMapper; } - float getMappedValue() const; + double getMappedValue() const; void setShowToolTip(bool show); @@ -61,7 +61,7 @@ void setTracking(bool tracking); void setShowScale(bool show); void setValue(int value); - void setMappedValue(float mappedValue); + void setMappedValue(double mappedValue); void scroll(bool up); void resetToDefault(); @@ -82,7 +82,7 @@ int m_max; int m_default; int m_value; - float m_mappedValue; + double m_mappedValue; bool m_noMappedUpdate; float m_rotation; Qt::Orientation m_orientation; diff -r 282f4be8f058 -r c02c51ae5238 widgets/TipDialog.cpp --- a/widgets/TipDialog.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/TipDialog.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -105,7 +105,7 @@ TipDialog::previous() { if (--m_tipNumber < 0) { - m_tipNumber = m_tips.size() - 1; + m_tipNumber = int(m_tips.size()) - 1; } showTip(); diff -r 282f4be8f058 -r c02c51ae5238 widgets/TransformFinder.cpp --- a/widgets/TransformFinder.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/TransformFinder.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -201,7 +201,7 @@ j != sorted.begin(); ) { --j; m_sortedResults.push_back(*j); - if (m_sortedResults.size() == maxResults) break; + if ((int)m_sortedResults.size() == maxResults) break; } if (m_sortedResults.empty()) m_selectedTransform = ""; @@ -209,7 +209,7 @@ m_upToDateCount = 0; - for (int j = m_labels.size(); j > m_sortedResults.size(); ) { + for (int j = (int)m_labels.size(); j > (int)m_sortedResults.size(); ) { m_labels[--j]->hide(); } @@ -225,19 +225,19 @@ if (m_sortedResults.size() < sorted.size()) { m_infoLabel->setText (tr("Found %n description(s) containing %1, showing the first %2 only", - 0, sorted.size()).arg(text).arg(m_sortedResults.size())); + 0, int(sorted.size())).arg(text).arg(m_sortedResults.size())); } else { m_infoLabel->setText (tr("Found %n description(s) containing %1", - 0, sorted.size()).arg(text)); + 0, int(sorted.size())).arg(text)); } return; } - if (m_upToDateCount >= m_sortedResults.size()) return; + if (m_upToDateCount >= (int)m_sortedResults.size()) return; - while (m_upToDateCount < m_sortedResults.size()) { + while (m_upToDateCount < (int)m_sortedResults.size()) { int i = m_upToDateCount; @@ -302,7 +302,7 @@ } selectedText += tr(""); - if (i >= m_labels.size()) { + if (i >= (int)m_labels.size()) { SelectableLabel *label = new SelectableLabel(m_resultsFrame); m_resultsLayout->addWidget(label); connect(label, SIGNAL(selectionChanged()), this, @@ -338,7 +338,7 @@ { QObject *s = sender(); m_selectedTransform = ""; - for (int i = 0; i < m_labels.size(); ++i) { + for (int i = 0; i < (int)m_labels.size(); ++i) { if (!m_labels[i]->isVisible()) continue; if (m_labels[i] == s) { if (m_labels[i]->isSelected()) { @@ -374,7 +374,7 @@ void TransformFinder::up() { - for (int i = 0; i < m_labels.size(); ++i) { + for (int i = 0; i < (int)m_labels.size(); ++i) { if (!m_labels[i]->isVisible()) continue; if (m_labels[i]->objectName() == m_selectedTransform) { if (i > 0) { @@ -390,10 +390,10 @@ void TransformFinder::down() { - for (int i = 0; i < m_labels.size(); ++i) { + for (int i = 0; i < (int)m_labels.size(); ++i) { if (!m_labels[i]->isVisible()) continue; if (m_labels[i]->objectName() == m_selectedTransform) { - if (i+1 < m_labels.size() && + if (i+1 < (int)m_labels.size() && m_labels[i+1]->isVisible()) { m_labels[i]->setSelected(false); m_labels[i+1]->setSelected(true); diff -r 282f4be8f058 -r c02c51ae5238 widgets/UnitConverter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/UnitConverter.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,401 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "UnitConverter.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "base/Debug.h" +#include "base/Pitch.h" +#include "base/Preferences.h" + +using namespace std; + +static QString pianoNotes[] = { + "C", "C# / Db", "D", "D# / Eb", "E", + "F", "F# / Gb", "G", "G# / Ab", "A", "A# / Bb", "B" +}; + +UnitConverter::UnitConverter(QWidget *parent) : + QDialog(parent) +{ + QGridLayout *maingrid = new QGridLayout; + setLayout(maingrid); + + QTabWidget *tabs = new QTabWidget; + maingrid->addWidget(tabs, 0, 0); + + QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close); + maingrid->addWidget(bb, 1, 0); + connect(bb, SIGNAL(rejected()), this, SLOT(close())); + + QFrame *frame = new QFrame; + tabs->addTab(frame, tr("Pitch")); + + QGridLayout *grid = new QGridLayout; + frame->setLayout(grid); + + m_freq = new QDoubleSpinBox; + m_freq->setSuffix(QString(" Hz")); + m_freq->setDecimals(6); + m_freq->setMinimum(1e-3); + m_freq->setMaximum(1e6); + m_freq->setValue(440); + connect(m_freq, SIGNAL(valueChanged(double)), + this, SLOT(freqChanged())); + + // The min and max range values for all the remaining controls are + // determined by the min and max Hz above + + m_midi = new QSpinBox; + m_midi->setMinimum(-156); + m_midi->setMaximum(203); + connect(m_midi, SIGNAL(valueChanged(int)), + this, SLOT(midiChanged())); + + m_note = new QComboBox; + for (int i = 0; i < 12; ++i) { + m_note->addItem(pianoNotes[i]); + } + connect(m_note, SIGNAL(currentIndexChanged(int)), + this, SLOT(noteChanged())); + + m_octave = new QSpinBox; + m_octave->setMinimum(-14); + m_octave->setMaximum(15); + connect(m_octave, SIGNAL(valueChanged(int)), + this, SLOT(octaveChanged())); + + m_cents = new QDoubleSpinBox; + m_cents->setSuffix(tr(" cents")); + m_cents->setDecimals(4); + m_cents->setMinimum(-50); + m_cents->setMaximum(50); + connect(m_cents, SIGNAL(valueChanged(double)), + this, SLOT(centsChanged())); + + int row = 0; + + grid->addWidget(new QLabel(tr("In 12-tone Equal Temperament:")), row, 0, 1, 9); + + ++row; + + grid->setRowMinimumHeight(row, 8); + + ++row; + + grid->addWidget(m_freq, row, 0, 2, 1, Qt::AlignRight | Qt::AlignVCenter); + grid->addWidget(new QLabel(tr("=")), row, 1, 2, 1, Qt::AlignHCenter | Qt::AlignVCenter); + + grid->addWidget(new QLabel(tr("+")), row, 7, 2, 1, Qt::AlignHCenter | Qt::AlignVCenter); + grid->addWidget(m_cents, row, 8, 2, 1, Qt::AlignLeft | Qt::AlignVCenter); + + grid->addWidget(new QLabel(tr("Piano note")), row, 2, 1, 2); + grid->addWidget(m_note, row, 4); + grid->addWidget(new QLabel(tr("in octave")), row, 5); + grid->addWidget(m_octave, row, 6); + + ++row; + + grid->addWidget(new QLabel(tr("MIDI pitch")), row, 2, 1, 2); + grid->addWidget(m_midi, row, 4); + + ++row; + + grid->setRowStretch(row, 20); + grid->setRowMinimumHeight(row, 8); + + ++row; + + m_pitchPrefsLabel = new QLabel; + grid->addWidget(m_pitchPrefsLabel, row, 0, 1, 9); + + ++row; + + grid->addWidget + (new QLabel(tr("Note that only pitches in the range 0 to 127 are valid " + "in the MIDI protocol.")), + row, 0, 1, 9); + + ++row; + + frame = new QFrame; + tabs->addTab(frame, tr("Tempo")); + + grid = new QGridLayout; + frame->setLayout(grid); + + m_samples = new QDoubleSpinBox; + m_samples->setSuffix(QString(" samples")); + m_samples->setDecimals(2); + m_samples->setMinimum(1); + m_samples->setMaximum(1e8); + m_samples->setValue(22050); + connect(m_samples, SIGNAL(valueChanged(double)), + this, SLOT(samplesChanged())); + + m_period = new QDoubleSpinBox; + m_period->setSuffix(QString(" ms")); + m_period->setDecimals(4); + m_period->setMinimum(1e-3); + m_period->setMaximum(100000); + m_period->setValue(500); + connect(m_period, SIGNAL(valueChanged(double)), + this, SLOT(periodChanged())); + + m_bpm = new QDoubleSpinBox; + m_bpm->setSuffix(QString(" bpm")); + m_bpm->setDecimals(4); + m_bpm->setMinimum(0.1); + m_bpm->setMaximum(1e6); + m_bpm->setValue(120); + connect(m_bpm, SIGNAL(valueChanged(double)), + this, SLOT(bpmChanged())); + + m_tempofreq = new QDoubleSpinBox; + m_tempofreq->setSuffix(QString(" beats/sec")); + m_tempofreq->setDecimals(4); + m_tempofreq->setMinimum(1e-3); + m_tempofreq->setMaximum(1e5); + m_tempofreq->setValue(0.5); + + connect(m_tempofreq, SIGNAL(valueChanged(double)), + this, SLOT(tempofreqChanged())); + + m_samplerate = new QComboBox; + QList rates; + rates << 8000; + for (int i = 1; i <= 16; i *= 2) { + rates << 11025 * i << 12000 * i; + } + foreach (int r, rates) { + m_samplerate->addItem(QString("%1 Hz").arg(r)); + } + connect(m_samplerate, SIGNAL(currentIndexChanged(int)), + this, SLOT(samplerateChanged())); + m_samplerate->setCurrentText("44100 Hz"); + + connect(Preferences::getInstance(), + SIGNAL(propertyChanged(PropertyContainer::PropertyName)), + this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); + + row = 0; + + grid->setRowStretch(row, 20); + grid->setRowMinimumHeight(row, 8); + + ++row; + + grid->addWidget(new QLabel(tr("Beat period")), row, 0, 2, 1, Qt::AlignVCenter); + grid->addWidget(m_period, row, 1); + grid->addWidget(new QLabel(tr("=")), row, 2, 2, 1, Qt::AlignVCenter); + + grid->addWidget(m_tempofreq, row, 3); + + grid->addWidget(new QLabel(tr("at")), row, 4, 2, 1, Qt::AlignVCenter); + grid->addWidget(m_samplerate, row, 5, 2, 1, Qt::AlignVCenter); + + ++row; + + grid->addWidget(m_samples, row, 1); + grid->addWidget(m_bpm, row, 3); + + ++row; + + grid->setRowStretch(row, 20); + grid->setRowMinimumHeight(row, 8); + + updatePitchesFromFreq(); + updatePitchPrefsLabel(); + updateTempiFromSamples(); +} + +UnitConverter::~UnitConverter() +{ +} + +void +UnitConverter::setTo(QSpinBox *box, int value) +{ + box->blockSignals(true); + if (value < box->minimum() || value > box->maximum()) { + QPalette p; + p.setColor(QPalette::Text, Qt::red); + box->setPalette(p); + } else { + box->setPalette(QPalette()); + } + box->setValue(value); + box->blockSignals(false); +} + +void +UnitConverter::setTo(QDoubleSpinBox *box, double value) +{ + box->blockSignals(true); + if (value < box->minimum() || value > box->maximum()) { + QPalette p; + p.setColor(QPalette::Text, Qt::red); + box->setPalette(p); + } else { + box->setPalette(QPalette()); + } + box->setValue(value); + box->blockSignals(false); +} + +void +UnitConverter::preferenceChanged(PropertyContainer::PropertyName) +{ + updatePitchesFromFreq(); + updatePitchPrefsLabel(); +} + +void +UnitConverter::updatePitchPrefsLabel() +{ + m_pitchPrefsLabel->setText + (tr("With concert-A tuning frequency at %1 Hz, and " + "middle C residing in octave %2.\n" + "(These can be changed in the application preferences.)") + .arg(Preferences::getInstance()->getTuningFrequency()) + .arg(Preferences::getInstance()->getOctaveOfMiddleC())); +} + +void +UnitConverter::freqChanged() +{ + updatePitchesFromFreq(); +} + +void +UnitConverter::midiChanged() +{ + double freq = Pitch::getFrequencyForPitch(m_midi->value(), m_cents->value()); + m_freq->setValue(freq); +} + +void +UnitConverter::noteChanged() +{ + int pitch = Pitch::getPitchForNoteAndOctave(m_note->currentIndex(), + m_octave->value()); + double freq = Pitch::getFrequencyForPitch(pitch, m_cents->value()); + m_freq->setValue(freq); +} + +void +UnitConverter::octaveChanged() +{ + int pitch = Pitch::getPitchForNoteAndOctave(m_note->currentIndex(), + m_octave->value()); + double freq = Pitch::getFrequencyForPitch(pitch, m_cents->value()); + m_freq->setValue(freq); +} + +void +UnitConverter::centsChanged() +{ + double freq = Pitch::getFrequencyForPitch(m_midi->value(), m_cents->value()); + m_freq->setValue(freq); +} + +void +UnitConverter::updatePitchesFromFreq() +{ + double cents = 0; + int pitch = Pitch::getPitchForFrequency(m_freq->value(), ¢s); + int note, octave; + Pitch::getNoteAndOctaveForPitch(pitch, note, octave); + + cerr << "pitch " << pitch << " note " << note << " octave " << octave << " cents " << cents << endl; + + setTo(m_midi, pitch); + setTo(m_cents, cents); + setTo(m_octave, octave); + + m_note->blockSignals(true); + m_note->setCurrentIndex(note); + m_note->blockSignals(false); +} + +void +UnitConverter::samplesChanged() +{ + updateTempiFromSamples(); +} + +void +UnitConverter::periodChanged() +{ + double rate = getSampleRate(); + double sec = m_period->value() / 1000.0; + double samples = rate * sec; + m_samples->setValue(samples); +} + +void +UnitConverter::bpmChanged() +{ + double rate = getSampleRate(); + double sec = 60.0 / m_bpm->value(); + double samples = rate * sec; + m_samples->setValue(samples); +} + +void +UnitConverter::tempofreqChanged() +{ + double rate = getSampleRate(); + double samples = rate / m_tempofreq->value(); + m_samples->setValue(samples); +} + +void +UnitConverter::samplerateChanged() +{ + // Preserve the beat period in seconds, here, not in samples + periodChanged(); +} + +double +UnitConverter::getSampleRate() +{ + return double(atoi(m_samplerate->currentText().toLocal8Bit().data())); +} + +void +UnitConverter::updateTempiFromSamples() +{ + double samples = m_samples->value(); + double rate = getSampleRate(); + + cerr << samples << " samples at rate " << rate << endl; + + double sec = samples / rate; + double hz = rate / samples; + double bpm = 60.0 / sec; + + setTo(m_bpm, bpm); + setTo(m_period, sec * 1000.0); + setTo(m_tempofreq, hz); +} + + diff -r 282f4be8f058 -r c02c51ae5238 widgets/UnitConverter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/UnitConverter.h Wed Apr 20 12:06:28 2016 +0100 @@ -0,0 +1,73 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef UNIT_CONVERTER_H +#define UNIT_CONVERTER_H + +#include + +#include "base/PropertyContainer.h" + +class QSpinBox; +class QDoubleSpinBox; +class QComboBox; +class QLabel; + +class UnitConverter : public QDialog +{ + Q_OBJECT + +public: + UnitConverter(QWidget *parent = 0); + virtual ~UnitConverter(); + +private slots: + void freqChanged(); + void midiChanged(); + void noteChanged(); + void octaveChanged(); + void centsChanged(); + + void samplesChanged(); + void periodChanged(); + void bpmChanged(); + void tempofreqChanged(); + void samplerateChanged(); + + void preferenceChanged(PropertyContainer::PropertyName); + +private: + QDoubleSpinBox *m_freq; + QSpinBox *m_midi; + QComboBox *m_note; + QSpinBox *m_octave; + QDoubleSpinBox *m_cents; + QLabel *m_pitchPrefsLabel; + void updatePitchesFromFreq(); + void updatePitchPrefsLabel(); + + QDoubleSpinBox *m_samples; + QDoubleSpinBox *m_period; + QDoubleSpinBox *m_bpm; + QDoubleSpinBox *m_tempofreq; + QComboBox *m_samplerate; + void updateTempiFromSamples(); + + double getSampleRate(); + + void setTo(QSpinBox *, int); + void setTo(QDoubleSpinBox *, double); +}; + +#endif diff -r 282f4be8f058 -r c02c51ae5238 widgets/WindowShapePreview.cpp --- a/widgets/WindowShapePreview.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/WindowShapePreview.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -32,7 +32,7 @@ WindowShapePreview::WindowShapePreview(QWidget *parent) : QFrame(parent), - m_windowType(WindowType(999)) + m_windowType(HanningWindow) { QHBoxLayout *layout = new QHBoxLayout; layout->setMargin(0); @@ -51,7 +51,7 @@ WindowShapePreview::updateLabels() { int step = 24; - int peak = 48; + float peak = 48; int w = step * 4, h = 64; WindowType type = m_windowType; Window windower = Window(type, step * 2); @@ -62,8 +62,8 @@ QPainterPath path; - path.moveTo(0, h - peak + 1); - path.lineTo(w, h - peak + 1); + path.moveTo(0, float(h) - peak + 1); + path.lineTo(w, float(h) - peak + 1); timePainter.setPen(Qt::gray); timePainter.setRenderHint(QPainter::Antialiasing, true); @@ -84,7 +84,7 @@ } } for (int i = 0; i < w; ++i) { - int y = h - int(peak * acc[i] + 0.001) + 1; + int y = h - int(peak * acc[i] + 0.001f) + 1; if (i == 0) path.moveTo(i, y); else path.lineTo(i, y); } @@ -150,7 +150,7 @@ float power = output[i][0] * output[i][0] + output[i][1] * output[i][1]; float db = mindb; if (power > 0) { - db = 20 * log10(power); + db = 20.f * log10f(power); if (first || db > maxdb) maxdb = db; if (first || db < mindb) mindb = db; first = false; @@ -167,8 +167,8 @@ // float ly = h - ((-80.f + -mindb) / maxval) * peak + 1; - path.moveTo(0, h - peak + 1); - path.lineTo(fw, h - peak + 1); + path.moveTo(0, float(h) - peak + 1); + path.lineTo(fw, float(h) - peak + 1); freqPainter.setPen(Qt::gray); freqPainter.setRenderHint(QPainter::Antialiasing, true); @@ -181,12 +181,12 @@ for (int i = 0; i < fftsize/2; ++i) { float power = output[i][0] * output[i][0] + output[i][1] * output[i][1]; - float db = 20 * log10(power); + float db = 20.f * log10f(power); float val = db + -mindb; if (val < 0) val = 0; float norm = val / maxval; - float x = (fw / float(fftsize/2)) * i; - float y = h - norm * peak + 1; + float x = (float(fw) / float(fftsize/2)) * float(i); + float y = float(h) - norm * peak + 1; if (i == 0) path.moveTo(x, y); else path.lineTo(x, y); } @@ -209,7 +209,6 @@ void WindowShapePreview::setWindowType(WindowType type) { - if (m_windowType == type) return; m_windowType = type; updateLabels(); } diff -r 282f4be8f058 -r c02c51ae5238 widgets/WindowTypeSelector.cpp --- a/widgets/WindowTypeSelector.cpp Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/WindowTypeSelector.cpp Wed Apr 20 12:06:28 2016 +0100 @@ -22,9 +22,23 @@ #include "base/Preferences.h" -WindowTypeSelector::WindowTypeSelector(WindowType defaultType, QWidget *parent) : - QFrame(parent), - m_windowType(WindowType(999)) +WindowTypeSelector::WindowTypeSelector(WindowType defaultType) +{ + init(defaultType); +} + +WindowTypeSelector::WindowTypeSelector() +{ + Preferences *prefs = Preferences::getInstance(); + int min = 0, max = 0, deflt = 0; + WindowType type = + WindowType(prefs->getPropertyRangeAndValue("Window Type", &min, &max, + &deflt)); + init(type); +} + +void +WindowTypeSelector::init(WindowType defaultType) { QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); @@ -48,15 +62,10 @@ m_windowShape = new WindowShapePreview; m_windowCombo = new QComboBox; - int min = 0, max = 0, deflt = 0, i = 0; int window = int(defaultType); - if (window == 999) { - window = prefs->getPropertyRangeAndValue("Window Type", &min, &max, - &deflt); - } int index = 0; - for (i = 0; i <= 8; ++i) { + for (int i = 0; i <= 8; ++i) { m_windowCombo->addItem(prefs->getPropertyValueLabel("Window Type", m_windows[i])); if (m_windows[i] == window) index = i; @@ -69,7 +78,9 @@ connect(m_windowCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(windowIndexChanged(int))); - windowIndexChanged(index); + + m_windowType = defaultType; + m_windowShape->setWindowType(m_windowType); } WindowTypeSelector::~WindowTypeSelector() diff -r 282f4be8f058 -r c02c51ae5238 widgets/WindowTypeSelector.h --- a/widgets/WindowTypeSelector.h Tue Jul 14 15:04:46 2015 +0100 +++ b/widgets/WindowTypeSelector.h Wed Apr 20 12:06:28 2016 +0100 @@ -28,8 +28,8 @@ Q_OBJECT public: - WindowTypeSelector(WindowType defaultType = WindowType(999), // 999 -> get from preferences - QWidget *parent = 0); + WindowTypeSelector(WindowType defaultType); + WindowTypeSelector(); // get window type from preferences virtual ~WindowTypeSelector(); WindowType getWindowType() const; @@ -48,6 +48,8 @@ WindowShapePreview *m_windowShape; WindowType *m_windows; WindowType m_windowType; + + void init(WindowType type); }; #endif diff -r 282f4be8f058 -r c02c51ae5238 widgets/widgets.pro --- a/widgets/widgets.pro Tue Jul 14 15:04:46 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -TEMPLATE = lib - -SV_UNIT_PACKAGES = vamp-hostsdk fftw3f -load(../prf/sv.prf) - -CONFIG += sv staticlib qt thread warn_on stl rtti exceptions -QT += xml - -TARGET = svwidgets - -DEPENDPATH += . .. -INCLUDEPATH += . .. -OBJECTS_DIR = tmp_obj -MOC_DIR = tmp_moc - -# Input -HEADERS += ActivityLog.h \ - AudioDial.h \ - ClickableLabel.h \ - ColourNameDialog.h \ - CommandHistory.h \ - CSVFormatDialog.h \ - Fader.h \ - InteractiveFileFinder.h \ - IconLoader.h \ - ImageDialog.h \ - ItemEditDialog.h \ - KeyReference.h \ - LabelCounterInputDialog.h \ - LayerTree.h \ - LayerTreeDialog.h \ - LEDButton.h \ - ListInputDialog.h \ - MIDIFileImportDialog.h \ - ModelDataTableDialog.h \ - NotifyingCheckBox.h \ - NotifyingComboBox.h \ - NotifyingPushButton.h \ - NotifyingTabBar.h \ - Panner.h \ - PluginParameterBox.h \ - PluginParameterDialog.h \ - ProgressDialog.h \ - PropertyBox.h \ - PropertyStack.h \ - RangeInputDialog.h \ - SelectableLabel.h \ - SubdividingMenu.h \ - TextAbbrev.h \ - Thumbwheel.h \ - TipDialog.h \ - TransformFinder.h \ - WindowShapePreview.h \ - WindowTypeSelector.h -SOURCES += ActivityLog.cpp \ - AudioDial.cpp \ - ColourNameDialog.cpp \ - CommandHistory.cpp \ - CSVFormatDialog.cpp \ - Fader.cpp \ - InteractiveFileFinder.cpp \ - IconLoader.cpp \ - ImageDialog.cpp \ - ItemEditDialog.cpp \ - KeyReference.cpp \ - LabelCounterInputDialog.cpp \ - LayerTree.cpp \ - LayerTreeDialog.cpp \ - LEDButton.cpp \ - ListInputDialog.cpp \ - MIDIFileImportDialog.cpp \ - ModelDataTableDialog.cpp \ - NotifyingCheckBox.cpp \ - NotifyingComboBox.cpp \ - NotifyingPushButton.cpp \ - NotifyingTabBar.cpp \ - Panner.cpp \ - PluginParameterBox.cpp \ - PluginParameterDialog.cpp \ - ProgressDialog.cpp \ - PropertyBox.cpp \ - PropertyStack.cpp \ - RangeInputDialog.cpp \ - SelectableLabel.cpp \ - SubdividingMenu.cpp \ - TextAbbrev.cpp \ - Thumbwheel.cpp \ - TipDialog.cpp \ - TransformFinder.cpp \ - WindowShapePreview.cpp \ - WindowTypeSelector.cpp