diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ae5e11c..daa622c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,495 +1,495 @@ ############################################################################# ## Kwave - CMakeLists.txt ## ------------------- ## begin : Tue May 01 2007 ## copyright : (C) 2007 by Thomas Eschenbacher ## email : Thomas.Eschenbacher@gmx.de ############################################################################# # ############################################################################# # # # Redistribution and use in source and binary forms, with or without # # modification, are permitted provided that the following conditions # # are met: # # # # 1. Redistributions of source code must retain the above copyright # # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # # notice, this list of conditions and the following disclaimer in the # # documentation and/or other materials provided with the distribution. # # # # For details see the accompanying cmake/COPYING-CMAKE-SCRIPTS file. # # # ############################################################################# ############################################################################# ### project name and version ### PROJECT(kwave) # KDE Application Version, managed by release script set (KDE_APPLICATIONS_VERSION_MAJOR "18") set (KDE_APPLICATIONS_VERSION_MINOR "07") set (KDE_APPLICATIONS_VERSION_MICRO "70") ############################################################################# ### build options: ### # OPTION(DEBUG "build debug code [default=off]" OFF) # OPTION(DEBUG_MEMORY "enable memory management debug code [default=off]" OFF) # OPTION(WITH_ALSA "enable playback/recording via ALSA [default=on]" ON) # OPTION(WITH_DOC "build online documentation [default=on]" ON) # OPTION(WITH_FLAC "enable support for FLAC files [default=on]" ON) # OPTION(WITH_MP3 "enable support for mp3 files [default=off]" OFF) # OPTION(WITH_OGG_OPUS "enable support for ogg/opus files [default=on]" ON) # OPTION(WITH_OGG_VORBIS "enable support for ogg/vorbis files [default=on]" ON) # OPTION(WITH_OSS "enable playback/recording via OSS [default=on]" ON) # OPTION(WITH_OPTIMIZED_MEMCPY "enable optimized memcpy [default=on]" ON) # OPTION(WITH_PULSEAUDIO "enable playback/recording via PulseAudio [default=on]" ON) # OPTION(WITH_QT_AUDIO "enable playback via Qt Multimedia [default=on]" ON) ############################################################################# ### toplevel build targets: ### # all - default target, build all files # clean - clean up the current build directory # deb - create a debian package # doc - generate docbook files for online help # distfiles - generate subdirectory with all files for distribution # html_doc - generate HTML help (for the web) # install - install the package, with support for DESTDIR # msgstats - show the progress of translations # rpm - create a RPM package + a src.rpm file # src_rpm - create the source RPM only # tarball - create a tar.bz2 archive with the sources + specfile # uninstall - uninstall the package, with support for DESTDIR # update-translations - update translation files from KDE SVN # version-labels - update all version numbers and build dates # tarball - create a tar.gz file with the sources ############################################################################# ### required versions ### CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR) # Honor visibility properties for all target types (since cmake 3.3) IF (POLICY CMP0063) CMAKE_POLICY(SET CMP0063 NEW) ENDIF (POLICY CMP0063) # exclude generated automoc files per default (since cmake 3.10) IF (POLICY CMP0071) CMAKE_POLICY(SET CMP0071 NEW) ENDIF (POLICY CMP0071) SET(ECM_MIN_VERSION "1.7.0") SET(QT_MIN_VERSION "5.4.0") SET(KF5_MIN_VERSION "5.33.0") ############################################################################# ### path to additional cmake modules and includes ### SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) ############################################################################# ### Kwave version number ### INCLUDE(FindRequiredProgram) FIND_REQUIRED_PROGRAM(CAT_EXECUTABLE cat) SET(KWAVE_VERSION_MAJOR ${KDE_APPLICATIONS_VERSION_MAJOR}) SET(KWAVE_VERSION_MINOR ${KDE_APPLICATIONS_VERSION_MINOR}) SET(KWAVE_VERSION_MICRO ${KDE_APPLICATIONS_VERSION_MICRO}) SET(KWAVE_VERSION "${KWAVE_VERSION_MAJOR}.${KWAVE_VERSION_MINOR}.${KWAVE_VERSION_MICRO}") MESSAGE(STATUS "Building Kwave version ${KWAVE_VERSION}") ############################################################################# ### show the compiler name and version ### EXECUTE_PROCESS( COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE COMPILER_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) GET_FILENAME_COMPONENT(COMPILER_SHORT "${CMAKE_C_COMPILER}" NAME_WE CACHE) MESSAGE(STATUS "Building with ${COMPILER_SHORT} version ${COMPILER_VERSION}") ############################################################################# ### check for the CPU we build for ### EXECUTE_PROCESS( COMMAND ${CMAKE_C_COMPILER} -dumpmachine OUTPUT_VARIABLE MACHINE OUTPUT_STRIP_TRAILING_WHITESPACE ) MESSAGE(STATUS "Building for target ${MACHINE}") OPTION(WITH_OPTIMIZED_MEMCPY "enable optimized memcpy [default=on]" ON) IF (WITH_OPTIMIZED_MEMCPY) STRING(REGEX MATCH "(i.86-*)|(athlon-*)|(pentium-*)" _mach_x86 ${MACHINE}) IF (_mach_x86) MESSAGE(STATUS "Found target optimized memcpy() for X86 (from xine)") SET(ARCH_X86 1) ENDIF (_mach_x86) STRING(REGEX MATCH "(x86_64-*)|(X86_64-*)|(AMD64-*)|(amd64-*)" _mach_x86_64 ${MACHINE}) IF (_mach_x86_64) MESSAGE(STATUS "Found target optimized memcpy() for X86_64 (from xine)") SET(ARCH_X86_64 1) ENDIF (_mach_x86_64) STRING(REGEX MATCH "(ppc-*)|(powerpc-*)" _mach_ppc ${MACHINE}) IF (_mach_ppc) MESSAGE(STATUS "Found target optimized memcpy() for PPC (from xine)") SET(ARCH_PPC 1) ENDIF (_mach_ppc) IF (NOT ARCH_X86 AND NOT ARCH_X86_64 AND NOT ARCH_PPC) MESSAGE(STATUS "No platform specific memcpy available") ENDIF (NOT ARCH_X86 AND NOT ARCH_X86_64 AND NOT ARCH_PPC) ELSE (WITH_OPTIMIZED_MEMCPY) MESSAGE(STATUS "Platform specific memcpy is disabled") ENDIF (WITH_OPTIMIZED_MEMCPY) ############################################################################# ### cmake includes ### INCLUDE(CheckIncludeFiles) INCLUDE(CheckIncludeFilesCXX) INCLUDE(CheckCCompilerFlag) INCLUDE(CheckCXXCompilerFlag) INCLUDE(CheckTypeSize) INCLUDE(CheckFunctionExists) ############################################################################# ### toplevel compiler flags ### ADD_DEFINITIONS(-DHAVE_CONFIG_H) ADD_DEFINITIONS(-DQT_NO_CAST_TO_ASCII) ADD_DEFINITIONS(-DQT_NO_CAST_FROM_ASCII) ADD_DEFINITIONS(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) ADD_DEFINITIONS(-DQT_NO_URL_CAST_FROM_STRING) ADD_DEFINITIONS(-DQT_DEPRECATED_WARNINGS) ADD_DEFINITIONS(-DKXMLGUI_NO_DEPRECATED) CHECK_C_COMPILER_FLAG(" ${CMAKE_SHARED_LIBRARY_C_FLAGS}" C_HAVE_PIC) CHECK_CXX_COMPILER_FLAG(" ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}" CXX_HAVE_PIC) IF (CXX_HAVE_PIC AND C_HAVE_PIC) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_SHARED_LIBRARY_C_FLAGS}") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") ELSE (CXX_HAVE_PIC AND C_HAVE_PIC) MESSAGE(FATAL_ERROR "shared library support is missing") ENDIF (CXX_HAVE_PIC AND C_HAVE_PIC) CHECK_CXX_COMPILER_FLAG("--std=c++11" CXX_HAVE_CXX11) IF (CXX_HAVE_CXX11) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11") ENDIF (CXX_HAVE_CXX11) ############################################################################# LINK_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/libgui) LINK_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/libkwave) ############################################################################# ### Qt 5 support ### SET(CMAKE_AUTOMOC TRUE) SET(CMAKE_AUTOMOC_RELAXED_MODE FALSE) LIST(APPEND CMAKE_AUTOMOC_MACRO_NAMES "KWAVE_PLUGIN") FIND_PACKAGE(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Concurrent Core Widgets ) # Qt Multimedia support OPTION(WITH_QT_AUDIO "enable playback via Qt Multimedia [default=on]" ON) IF (WITH_QT_AUDIO) SET(HAVE_QT_AUDIO_SUPPORT on) FIND_PACKAGE(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Multimedia ) ENDIF (WITH_QT_AUDIO) ############################################################################# ### KF5 support ### FIND_PACKAGE(ECM ${ECM_MIN_VERSION} REQUIRED NO_MODULE) SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ) INCLUDE(KDEInstallDirs) INCLUDE(KDECompilerSettings) INCLUDE(KDECMakeSettings) INCLUDE(GenerateExportHeader) INCLUDE(FeatureSummary) FIND_PACKAGE(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Archive Completion Config ConfigWidgets CoreAddons Crash DBusAddons DocTools I18n IconThemes KIO Service TextWidgets XmlGui WidgetsAddons ) IF (NOT WITH_DOC) SET_PACKAGE_PROPERTIES(KF5DocTools PROPERTIES DESCRIPTION "Tools to generate documentation" TYPE OPTIONAL ) ENDIF (NOT WITH_DOC) FEATURE_SUMMARY(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) INCLUDE_DIRECTORIES( ${INTERFACE_INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) ############################################################################# ### checks for needed header files ### CHECK_INCLUDE_FILES(sys/times.h HAVE_SYS_TIMES_H) CHECK_INCLUDE_FILES(signal.h HAVE_SIGNAL_H) -SET(_inc_c errno.h limits.h math.h signal.h - stdint.h stdio.h stdlib.h string.h sys/types.h unistd.h pthread.h) +SET(_inc_c errno.h math.h signal.h + stdint.h stdio.h stdlib.h string.h unistd.h pthread.h) CHECK_INCLUDE_FILES("${_inc_c}" HAVE_REQUIRED_STD_C_HEADERS) IF (NOT HAVE_REQUIRED_STD_C_HEADERS) MESSAGE(FATAL_ERROR " unable to find one or more of the following C header files: ${_inc_c}") ENDIF (NOT HAVE_REQUIRED_STD_C_HEADERS) -SET(_inc_cpp algorithm limits new) +SET(_inc_cpp algorithm complex limits new) CHECK_INCLUDE_FILES_CXX("${_inc_cpp}") ############################################################################# ### checks for some functions ### CHECK_FUNCTION_EXISTS(unlink HAVE_UNLINK) INCLUDE(KwaveSysinfo) ############################################################################# ### libaudiofile and libsamplerate support ### INCLUDE(KwaveLibaudiofileSupport) INCLUDE(KwaveLibsamplerateSupport) ############################################################################# ### optionally: OSS, ALSA and PulseAudio support ### ### for playback/recording ### INCLUDE(KwaveOSSSupport) INCLUDE(KwaveALSASupport) INCLUDE(KwavePulseAudioSupport) ############################################################################# ### cmake includes ### INCLUDE(KwaveL10N) ############################################################################# ### flags for debugging ### OPTION(DEBUG "enable the debug plugin in the menu [default=off]" OFF) IF (DEBUG) SET(HAVE_DEBUG_PLUGIN ON CACHE BOOL "enable debug plugin in the menu") ENDIF (DEBUG) OPTION(DEBUG_MEMORY "enable memory management debug code [default=off]" OFF) ############################################################################# ### subdirs ### ADD_SUBDIRECTORY( libgui ) ADD_SUBDIRECTORY( libkwave ) ADD_SUBDIRECTORY( kwave ) ADD_SUBDIRECTORY( plugins ) OPTION(WITH_DOC "build online documentation [default=on]" ON) IF (WITH_DOC) ADD_SUBDIRECTORY( doc ) ENDIF (WITH_DOC) ############################################################################# ### RPM support ### # directory that receives the files of the "distfiles" target SET(DISTFILES_DIR ${CMAKE_BINARY_DIR}/v${KWAVE_VERSION}) FILE(MAKE_DIRECTORY ${DISTFILES_DIR}) INCLUDE(KwaveRPMSupport) ############################################################################# ### DEB support ### INCLUDE(KwaveDEBSupport) ############################################################################# CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ) ############################################################################# ### KDE .desktop file / mime types ### SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/wav;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-wav;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/basic;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-8svx;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-aifc;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-aiff;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-avr;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-caf;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-ircam;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-nist;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-smp;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-voc;") IF (WITH_MP3) SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/mpeg;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-mp1;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-mp2;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-mp3;") ENDIF (WITH_MP3) IF (WITH_FLAC) SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}application/x-flac;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-flac;") ENDIF (WITH_FLAC) IF (WITH_OGG_OPUS OR WITH_OGG_VORBIS) SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/ogg;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}application/ogg;") ENDIF (WITH_OGG_OPUS OR WITH_OGG_VORBIS) IF (WITH_OGG_VORBIS) SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-ogg;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}application/x-ogg;") SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/x-vorbis+ogg;") ENDIF (WITH_OGG_VORBIS) IF (WITH_OGG_OPUS) SET(KWAVE_DESKTOP_MIME_TYPES "${KWAVE_DESKTOP_MIME_TYPES}audio/opus;") ENDIF (WITH_OGG_OPUS) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/kwave/org.kde.kwave.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/kwave/org.kde.kwave.desktop ) ############################################################################# ### Gentoo support ### INCLUDE(KwaveEbuild) ############################################################################# ### collection of all files used for distribution ### FIND_PROGRAM(SED_EXECUTABLE NAMES sed) SET(_distfiles_md5sum ${DISTFILES_DIR}/MD5SUMS-${KWAVE_VERSION}) ADD_CUSTOM_COMMAND(OUTPUT ${_distfiles_md5sum} COMMAND md5sum ${KWAVE_DISTFILES} | ${SED_EXECUTABLE} s+${DISTFILES_DIR}/++g > ${_distfiles_md5sum} DEPENDS ${KWAVE_DISTFILES} ) ADD_CUSTOM_TARGET(distfiles DEPENDS ${KWAVE_DISTFILES} ${_distfiles_md5sum} ) ############################################################################# ### uninstall support ### FIND_REQUIRED_PROGRAM(RMDIR_EXECUTABLE rmdir) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/uninstall.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY ) ############################################################################# ### "make apidoc" ### FIND_PROGRAM(DOXYGEN_EXECUTABLE doxygen) IF (DOXYGEN_EXECUTABLE) SET(DOXYFILE ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) SET(DOXYGEN_OUTPUT_DIR ${CMAKE_BINARY_DIR}/doc/api) SET(DOXYGEN_LOGFILE ${CMAKE_CURRENT_BINARY_DIR}/doxygen.log) MESSAGE(STATUS "Found doxygen: ${DOXYGEN_EXECUTABLE}") CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/doxy.cfg.in ${DOXYFILE} @ONLY ) ADD_CUSTOM_TARGET(apidoc DEPENDS html_doc_devel COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_BINARY_DIR}/doc/api" COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_BINARY_DIR}/doc/api" COMMAND "${DOXYGEN_EXECUTABLE}" "${DOXYFILE}" COMMAND "${CAT_EXECUTABLE}" "${DOXYGEN_LOGFILE}" DEPENDS ${DOXYFILE} ) SET(KWAVE_ADDITIONAL_CLEAN_FILES ${KWAVE_ADDITIONAL_CLEAN_FILES} ${DOXYGEN_OUTPUT_DIR} ${DOXYGEN_LOGFILE} ) ENDIF (DOXYGEN_EXECUTABLE) ############################################################################# ### additional files for "make clean" ### IF (KWAVE_ADDITIONAL_CLEAN_FILES) SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${KWAVE_ADDITIONAL_CLEAN_FILES}" ) ENDIF (KWAVE_ADDITIONAL_CLEAN_FILES) ############################################################################# ### "make wrapper" ### ### -> creates a wrapper script named "kw" ### SET(WRAPPER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/kw") CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/bin/kwave.wrapper.in" "${WRAPPER_SCRIPT}" @ONLY ) ADD_CUSTOM_TARGET(wrapper COMMAND chmod +rx "${WRAPPER_SCRIPT}" DEPENDS "${WRAPPER_SCRIPT}" ) ############################################################################# MESSAGE(STATUS "Using CFLAGS=${CMAKE_C_FLAGS}") MESSAGE(STATUS "Using CXXFLAGS=${CMAKE_CXX_FLAGS}") MESSAGE(STATUS "Using LDFLAGS=${LDFLAGS}") ############################################################################# ############################################################################# diff --git a/libkwave/MemoryManager.cpp b/libkwave/MemoryManager.cpp index 6e6eba13..33dbf8a8 100644 --- a/libkwave/MemoryManager.cpp +++ b/libkwave/MemoryManager.cpp @@ -1,1108 +1,1107 @@ /*************************************************************************** MemoryManager.cpp - Manager for virtual and physical memory ------------------- begin : Wed Aug 08 2001 copyright : (C) 2000 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "config.h" #include -#include #include #include #include #include #include #include #include #ifdef HAVE_SYSINFO #include // for struct sysinfo #include // for sysinfo() #endif #ifdef HAVE_GETRLIMIT #include // for getrlimit() #endif #include "libkwave/MemoryManager.h" #include "libkwave/String.h" #include "libkwave/SwapFile.h" #include "libkwave/Utils.h" #include "libkwave/memcpy.h" /** number of elements in the m_cached_swap list */ #define CACHE_SIZE 16 /** static instance of the memory manager */ static Kwave::MemoryManager g_instance; /** last used handle */ Kwave::Handle Kwave::MemoryManager::m_last_handle = 0; //*************************************************************************** Kwave::MemoryManager::MemoryManager() :m_physical_limit(0), m_physical_max(0), m_virtual_limit(0), m_swap_dir(_("/tmp")), m_undo_limit(0), m_physical(), m_unmapped_swap(), m_mapped_swap(), m_cached_swap(), m_lock() { // reset statistics #ifdef DEBUG_MEMORY memset(&m_stats, 0x00, sizeof(m_stats)); #endif /* DEBUG_MEMORY */ // determine amount of physical memory // start with 1/4 of the theoretical address space // if sizeof(void *) == 4 -> 32 bit -> 1024 MB // if sizeof(void *) == 8 -> 64 bit -> 4.398E12 MB quint64 total = (1ULL << ((sizeof(void *) * 8ULL) - 22ULL)); // limit the total memory to a 32bit value [MB] if (total > (1ULL << 32)) total = (1ULL << 32) - 1; #ifdef DEBUG qDebug("Kwave::MemoryManager: theoretical limit: %llu MB", total); #endif /* DEBUG */ #ifdef HAVE_SYSINFO // get the physically installed memory quint64 installed_physical; struct sysinfo info; sysinfo(&info); // find out installed memory and convert to megabytes #ifdef HAVE_SYSINFO_MEMUNIT installed_physical = (info.totalram * info.mem_unit) >> 20; #ifdef DEBUG qDebug("Kwave::MemoryManager: sysinfo/memunit: %llu MB", installed_physical); #endif /* DEBUG */ #else /* HAVE_SYSINFO_MEMUNIT */ installed_physical = info.totalram >> 20; qDebug("Kwave::MemoryManager: sysinfo: %llu MB", installed_physical); #endif /* HAVE_SYSINFO_MEMUNIT */ if (installed_physical && (installed_physical < total)) total = installed_physical; #endif /* HAVE_SYSINFO */ #ifdef HAVE_GETRLIMIT struct rlimit limit; // check ulimit of data segment size if (getrlimit(RLIMIT_DATA, &limit) == 0) { quint64 physical_ulimit = qMin(limit.rlim_cur, limit.rlim_max) >> 20; #ifdef DEBUG qDebug("Kwave::MemoryManager: RLIMIT_DATA: %llu MB", physical_ulimit); #endif /* DEBUG */ if (physical_ulimit < total) total = physical_ulimit; } // check ulimit of total (virtual) system memory (address space) #ifdef RLIMIT_AS if (getrlimit(RLIMIT_AS, &limit) == 0) { quint64 total_ulimit = qMin(limit.rlim_cur, limit.rlim_max) >> 20; #ifdef DEBUG qDebug("Kwave::MemoryManager: RLIMIT_AS: %llu MB", total_ulimit); #endif /* DEBUG */ if (total_ulimit < total) total = total_ulimit; } #endif /* RLIMIT_AS */ #endif /* HAVE_GETRLIMIT */ // limit the total memory to a int value [MB] // (values go into the GUI) total = qMin(total, std::numeric_limits::max()); #ifdef DEBUG qDebug("Kwave::MemoryManager: => using up to %llu MB RAM", total); #endif /* DEBUG */ m_physical_max = total; m_physical_limit = total; #ifdef DEBUG_MEMORY m_stats.physical.limit = m_physical_limit << 20ULL; m_stats.swap.limit = m_virtual_limit << 20ULL; #endif /* DEBUG_MEMORY */ } //*************************************************************************** Kwave::MemoryManager::~MemoryManager() { close(); } //*************************************************************************** void Kwave::MemoryManager::close() { QMutexLocker lock(&m_lock); // print warnings for each physical memory block Q_ASSERT(m_physical.isEmpty()); // remove all remaining swap files and print warnings Q_ASSERT(m_unmapped_swap.isEmpty()); Q_ASSERT(m_mapped_swap.isEmpty()); Q_ASSERT(m_cached_swap.isEmpty()); } //*************************************************************************** Kwave::MemoryManager &Kwave::MemoryManager::instance() { return g_instance; } //*************************************************************************** void Kwave::MemoryManager::setPhysicalLimit(quint64 mb) { QMutexLocker lock(&m_lock); m_physical_limit = mb; mb = totalPhysical(); if (m_physical_limit > mb) m_physical_limit = mb; #ifdef DEBUG_MEMORY m_stats.physical.limit = m_physical_limit << 20ULL; #endif /* DEBUG_MEMORY */ } //*************************************************************************** void Kwave::MemoryManager::setVirtualLimit(quint64 mb) { QMutexLocker lock(&m_lock); m_virtual_limit = mb; #ifdef DEBUG_MEMORY m_stats.swap.limit = m_virtual_limit << 20ULL; #endif /* DEBUG_MEMORY */ /** @todo write a function to find out the limit of virtual memory */ // mb = totalVirtual(); // if (m_virtual_limit > mb) m_virtual_limit = mb; } //*************************************************************************** void Kwave::MemoryManager::setSwapDirectory(const QString &dir) { QMutexLocker lock(&m_lock); m_swap_dir = dir; } //*************************************************************************** void Kwave::MemoryManager::setUndoLimit(quint64 mb) { QMutexLocker lock(&m_lock); m_undo_limit = mb; mb = totalPhysical(); if (m_undo_limit > mb) m_undo_limit = mb; } //*************************************************************************** quint64 Kwave::MemoryManager::undoLimit() const { return m_undo_limit; } //*************************************************************************** quint64 Kwave::MemoryManager::totalPhysical() { return m_physical_max; } //*************************************************************************** Kwave::Handle Kwave::MemoryManager::newHandle() { for (unsigned int i = 0; i < std::numeric_limits::max(); i++) { // increment to get the next handle m_last_handle++; // allow only non-zero positive values (handle wraparound) if (m_last_handle <= 0) { m_last_handle = 0; continue; } // if handle is in use -> next one please... if (m_physical.contains(m_last_handle)) continue; if (m_mapped_swap.contains(m_last_handle)) continue; if (m_unmapped_swap.contains(m_last_handle)) continue; if (m_cached_swap.contains(m_last_handle)) continue; // valid number and not in use -> found a new one :-) return m_last_handle; } // if we reach this point all handles are in use :-( return 0; } //*************************************************************************** bool Kwave::MemoryManager::freePhysical(size_t size) { size_t freed = 0; if (m_physical.isEmpty()) return false; QList handles = m_physical.keys(); QMutableListIterator it(handles); it.toBack(); while (it.hasPrevious()) { Kwave::Handle handle = it.previous(); const physical_memory_t &p = m_physical[handle]; if (p.m_mapcount) continue; // in use :-( // convert to swapfile size_t s = p.m_size; #if 0 qDebug("Kwave::MemoryManager[%9d] - swapping %2u MB out to make "\ "space for %2u MB", handle, Kwave::toUint(s >> 20), Kwave::toUint(size >> 20)); #endif if (convertToVirtual(handle, s)) { freed += s; if (freed >= size) return true; // abort if the list is now empty if (m_physical.isEmpty()) return false; } } return false; } //*************************************************************************** Kwave::Handle Kwave::MemoryManager::allocate(size_t size) { QMutexLocker lock(&m_lock); Kwave::Handle handle = allocatePhysical(size); if (!handle) { // try to make some room in the physical memory area if (freePhysical(size)) { // and try again to allocate physical memory handle = allocatePhysical(size); } if (!handle) { // fallback: allocate a swapfile handle = allocateVirtual(size); } } #ifdef DEBUG_MEMORY if (!handle) { qWarning("Kwave::MemoryManager::allocate(%u) - out of memory!", Kwave::toUint(size)); } dump("allocate"); #endif /* DEBUG_MEMORY */ return handle; } //*************************************************************************** Kwave::Handle Kwave::MemoryManager::allocatePhysical(size_t size) { // check for limits quint64 limit = totalPhysical(); if (m_physical_limit < limit) limit = m_physical_limit; quint64 used = physicalUsed(); quint64 available = (used < limit) ? (limit - used) : 0; if ((size >> 20) >= available) return 0; // get a new handle Kwave::Handle handle = newHandle(); if (!handle) return 0; // out of handles :-( // try to allocate via malloc void *mem = ::malloc(size); if (!mem) return 0; // out of memory :-( // store the object in the list of physical objects physical_memory_t phys; phys.m_data = mem; phys.m_size = size; phys.m_mapcount = 0; Q_ASSERT(!m_physical.contains(handle)); m_physical.insert(handle, phys); #ifdef DEBUG_MEMORY m_stats.physical.handles++; m_stats.physical.allocs++; m_stats.physical.bytes += size; #endif /* DEBUG_MEMORY */ return handle; } //*************************************************************************** quint64 Kwave::MemoryManager::physicalUsed() { quint64 used = 0; foreach (const physical_memory_t &mem, m_physical.values()) used += (mem.m_size >> 10) + 1; return (used >> 10); } //*************************************************************************** quint64 Kwave::MemoryManager::virtualUsed() { quint64 used = 0; foreach (const Kwave::SwapFile *swapfile, m_cached_swap.values()) used += (swapfile->size() >> 10) + 1; foreach (const Kwave::SwapFile *swapfile, m_mapped_swap.values()) used += (swapfile->size() >> 10) + 1; foreach (const Kwave::SwapFile *swapfile, m_unmapped_swap.values()) used += (swapfile->size() >> 10) + 1; return (used >> 10); } //*************************************************************************** QString Kwave::MemoryManager::nextSwapFileName(Kwave::Handle handle) { QFileInfo file; QString filename; // these 6 'X' chars are needed for mkstemp ! filename = _("kwave-swapfile-%1-XXXXXX"); filename = filename.arg(static_cast(handle), 10, 10, QLatin1Char('0')); file.setFile(m_swap_dir, filename); return file.absoluteFilePath(); } //*************************************************************************** Kwave::Handle Kwave::MemoryManager::allocateVirtual(size_t size) { // shortcut, if virtual memory is disabled if (!m_virtual_limit) return 0; // check for limits quint64 limit = std::numeric_limits::max(); // totalVirtual() in MB if (m_virtual_limit < limit) limit = m_virtual_limit; quint64 used = virtualUsed(); // in MB quint64 available = (used < limit) ? (limit - used) : 0; if ((size >> 20) >= available) { qWarning("Kwave::MemoryManager::allocateVirtual(%u): out of memory, "\ "(used: %lluMB, available: %lluMB, limit=%lluMB)", Kwave::toUint(size), used, available, limit); dump("allocateVirtual"); return 0; } // get a new handle Kwave::Handle handle = newHandle(); if (!handle) return 0; // out of handles :-( // try to allocate Kwave::SwapFile *swap = new(std::nothrow) Kwave::SwapFile(nextSwapFileName(handle)); Q_ASSERT(swap); if (!swap) return 0; // out of memory :-( if (swap->allocate(size)) { // succeeded, store the object in our map m_unmapped_swap.insert(handle, swap); #ifdef DEBUG_MEMORY m_stats.swap.unmapped.bytes += size; m_stats.swap.unmapped.handles++; m_stats.swap.allocs++; #endif /* DEBUG_MEMORY */ return handle; } else { qWarning("Kwave::MemoryManager::allocateVirtual(%u): OOM, "\ "(used: %lluMB) - failed resizing swap file", Kwave::toUint(size), used); // failed: give up, delete the swapfile object delete swap; } return 0; } //*************************************************************************** bool Kwave::MemoryManager::convertToVirtual(Kwave::Handle handle, size_t new_size) { // check: it must be in physical space, otherwise the rest makes no sense Q_ASSERT(m_physical.contains(handle)); if (!m_physical.contains(handle)) return false; // get the old object in physical memory physical_memory_t mem = m_physical[handle]; Q_ASSERT(mem.m_data); Q_ASSERT(mem.m_size); if (!mem.m_data || !mem.m_size) return false; // allocate a new object, including a new handle // if successful it has been stored in m_unmapped_swap Kwave::Handle temp_handle = allocateVirtual(new_size); if (!temp_handle) return false; // copy old stuff to new location Kwave::SwapFile *swap = m_unmapped_swap[temp_handle]; Q_ASSERT(swap); swap->write(0, mem.m_data, Kwave::toUint(mem.m_size)); // free the old physical memory ::free(mem.m_data); m_physical.remove(handle); #ifdef DEBUG_MEMORY m_stats.physical.handles--; m_stats.physical.frees++; m_stats.physical.bytes -= mem.m_size; #endif /* DEBUG_MEMORY */ // discard the new (temporary) handle and re-use the old one m_unmapped_swap.remove(temp_handle); // temp_handle is now no longer valid m_unmapped_swap.insert(handle, swap); // we now have the old data with new size and old handle in m_unmapped_swap // qDebug("Kwave::MemoryManager[%9d] - moved to swap", handle); dump("convertToVirtual"); return true; } //*************************************************************************** bool Kwave::MemoryManager::convertToPhysical(Kwave::Handle handle, size_t new_size) { Q_ASSERT(new_size); if (!new_size) return false; // check: it must be in physical space, otherwise the rest makes no sense Q_ASSERT(m_unmapped_swap.contains(handle)); if (!m_unmapped_swap.contains(handle)) return false; Kwave::SwapFile *swap = m_unmapped_swap[handle]; Q_ASSERT(swap); if (!swap) return false; // allocate a new object, including a new handle // if successful it has been stored in m_physical Kwave::Handle temp_handle = allocatePhysical(new_size); if (!temp_handle) return false; physical_memory_t mem = m_physical[temp_handle]; Q_ASSERT(mem.m_data); Q_ASSERT(mem.m_size >= new_size); // copy old stuff to new location if (new_size <= swap->size()) { // shrinked swap->read(0, mem.m_data, Kwave::toUint(new_size)); } else { // grown swap->read(0, mem.m_data, Kwave::toUint(swap->size())); } #ifdef DEBUG_MEMORY m_stats.swap.unmapped.bytes -= swap->size(); m_stats.swap.unmapped.handles--; m_stats.swap.frees++; #endif /* DEBUG_MEMORY */ // free the old swapfile m_unmapped_swap.remove(handle); delete swap; // discard the new (temporary) handle and re-use the old one m_physical.remove(temp_handle); // temp_handle is now no longer valid m_physical.insert(handle, mem); // we now have the old data with new size and old handle in m_physical // qDebug("Kwave::MemoryManager[%9d] - reloaded %2u MB from swap", // handle, Kwave::toUint(mem.m_size >> 20)); dump("convertToPhysical"); return true; } //*************************************************************************** void Kwave::MemoryManager::tryToMakePhysical(Kwave::Handle handle) { if (!handle) return; if (m_physical.contains(handle)) return; // already ok if (m_mapped_swap.contains(handle)) return; // not allowed if (m_cached_swap.contains(handle)) return; // already fast enough Q_ASSERT(m_unmapped_swap.contains(handle)); if (!m_unmapped_swap.contains(handle)) return; const Kwave::SwapFile *swap = m_unmapped_swap[handle]; Q_ASSERT(swap); if (!swap) return; size_t size = swap->size(); quint64 limit = totalPhysical(); if (m_physical_limit < limit) limit = m_physical_limit; quint64 used = physicalUsed(); quint64 available = (used < limit) ? (limit - used) : 0; // if we would go over the physical limit... if ((size >> 20) >= available) { // ...try to swap out some old stuff if (!freePhysical(size)) return; } // try to convert the swapfile back to physical RAM convertToPhysical(handle, size); } //*************************************************************************** bool Kwave::MemoryManager::resize(Kwave::Handle handle, size_t size) { QMutexLocker lock(&m_lock); // qDebug("Kwave::MemoryManager[%9d] - resize to %u", handle, // Kwave::toUint(size)); // case 1: physical memory if (m_physical.contains(handle)) { const physical_memory_t phys_c = m_physical[handle]; // check: it must not be mmapped! Q_ASSERT(!phys_c.m_mapcount); if (phys_c.m_mapcount) return false; // if we are increasing: check if we get too large size_t current_size = phys_c.m_size; if ((size > current_size) && (physicalUsed() + ((size - current_size) >> 20) > m_physical_limit)) { // first try to swap out some old stuff m_physical[handle].m_mapcount++; bool physical_freed = freePhysical(size); m_physical[handle].m_mapcount--; if (!physical_freed) { // still too large -> move to virtual memory if (m_virtual_limit) { qDebug("Kwave::MemoryManager[%9d] - resize(%uMB) " "-> moving to swap", handle, Kwave::toUint(size >> 20)); return convertToVirtual(handle, size); } else { qDebug("Kwave::MemoryManager[%9d] - resize(%uMB) " "-> OOM", handle, Kwave::toUint(size >> 20)); return false; } } } // try to resize the physical memory physical_memory_t phys = m_physical[handle]; void *old_block = phys.m_data; void *new_block = ::realloc(old_block, size); if (new_block) { phys.m_data = new_block; phys.m_size = size; phys.m_mapcount = 0; m_physical[handle] = phys; #ifdef DEBUG_MEMORY m_stats.physical.bytes -= current_size; m_stats.physical.bytes += size; #endif /* DEBUG_MEMORY */ dump("resize"); return true; } else { // resizing failed, try to allocate virtual memory for it return convertToVirtual(handle, size); } } // case 2: mapped swapfile in cache -> unmap ! unmapFromCache(handle); // make sure it is not in the cache // case 3: mapped swapfile -> forbidden ! Q_ASSERT(!m_mapped_swap.contains(handle)); if (m_mapped_swap.contains(handle)) return false; // case 4: unmapped swapfile -> resize Q_ASSERT(m_unmapped_swap.contains(handle)); if (m_unmapped_swap.contains(handle)) { // try to find space in the physical memory if ((physicalUsed() + (size >> 20) > m_physical_limit)) { // free some space if necessary if (freePhysical(size)) { if (convertToPhysical(handle, size)) return true; } } else { // try to convert into the currently available phys. RAM if (convertToPhysical(handle, size)) return true; } // not enough free RAM: resize the pagefile // qDebug("Kwave::MemoryManager[%9d] - resize swap %u -> %u MB", // handle, // Kwave::toUint(swap->size() >> 20), // Kwave::toUint(size >> 20)); dump("resize"); Kwave::SwapFile *swap = m_unmapped_swap[handle]; #ifdef DEBUG_MEMORY size_t old_size = swap->size(); #endif /* DEBUG_MEMORY */ bool ok = swap->resize(size); if (!ok) return false; #ifdef DEBUG_MEMORY m_stats.swap.unmapped.bytes -= old_size; m_stats.swap.unmapped.bytes += size; #endif /* DEBUG_MEMORY */ return true; } return false; // nothing known about this object / invalid handle? } //*************************************************************************** size_t Kwave::MemoryManager::sizeOf(Kwave::Handle handle) { if (!handle) return 0; QMutexLocker lock(&m_lock); // case 1: physical memory if (m_physical.contains(handle)) { const physical_memory_t phys_c = m_physical[handle]; return phys_c.m_size; } // case 2: cached mapped swapfile if (m_cached_swap.contains(handle)) { const Kwave::SwapFile *swapfile = m_cached_swap[handle]; return swapfile->size(); } // case 3: mapped swapfile if (m_mapped_swap.contains(handle)) { const Kwave::SwapFile *swapfile = m_mapped_swap[handle]; return swapfile->size(); } // case 4: unmapped swapfile if (m_unmapped_swap.contains(handle)) { const Kwave::SwapFile *swapfile = m_unmapped_swap[handle]; return swapfile->size(); } return 0; } //*************************************************************************** void Kwave::MemoryManager::free(Kwave::Handle &handle) { if (!handle) return; QMutexLocker lock(&m_lock); // qDebug("Kwave::MemoryManager[%9d] - free", handle); if (m_physical.contains(handle)) { // physical memory (must not be mapped) Q_ASSERT(!m_physical[handle].m_mapcount); #ifdef DEBUG_MEMORY m_stats.physical.handles--; m_stats.physical.frees++; m_stats.physical.bytes -= m_physical[handle].m_size; #endif /* DEBUG_MEMORY */ void *b = m_physical[handle].m_data; Q_ASSERT(b); m_physical.remove(handle); ::free(b); handle = 0; dump("free"); return; } Q_ASSERT(!m_mapped_swap.contains(handle)); if (m_mapped_swap.contains(handle)) { // no-good: swapfile is still mapped !? unmap(handle); } unmapFromCache(handle); // make sure it is not in the cache if (m_unmapped_swap.contains(handle)) { // remove the pagefile Kwave::SwapFile *swap = m_unmapped_swap[handle]; #ifdef DEBUG_MEMORY m_stats.swap.unmapped.handles--; m_stats.swap.unmapped.bytes -= swap->size(); m_stats.swap.frees++; #endif /* DEBUG_MEMORY */ m_unmapped_swap.remove(handle); Q_ASSERT(!swap->mapCount()); delete swap; handle = 0; dump("free"); return; } Q_ASSERT(!handle); handle = 0; } //*************************************************************************** void *Kwave::MemoryManager::map(Kwave::Handle handle) { QMutexLocker lock(&m_lock); Q_ASSERT(handle); if (!handle) return Q_NULLPTR; // object not found ? // try to convert to physical RAM tryToMakePhysical(handle); // simple case: physical memory does not really need to be mapped if (m_physical.contains(handle)) { m_physical[handle].m_mapcount++; // qDebug("Kwave::MemoryManager[%9d] - mmap -> physical", handle); return m_physical[handle].m_data; } // no physical mem -> must be a swapfile // if it is already in the cache -> shortcut ! if (m_cached_swap.contains(handle)) { Kwave::SwapFile *swap = m_cached_swap[handle]; m_cached_swap.remove(handle); m_mapped_swap.insert(handle, swap); #ifdef DEBUG_MEMORY m_stats.swap.cached.handles--; m_stats.swap.cached.bytes -= swap->size(); m_stats.swap.mapped.handles++; m_stats.swap.mapped.bytes += swap->size(); #endif /* DEBUG_MEMORY */ // qDebug("Kwave::MemoryManager[%9d] - mmap -> cache hit", handle); Q_ASSERT(swap->mapCount() == 1); return swap->address(); } // other simple case: already mapped if (m_mapped_swap.contains(handle)) { Kwave::SwapFile *swap = m_mapped_swap[handle]; Q_ASSERT(swap->mapCount() >= 1); // qDebug("Kwave::MemoryManager[%9d] - mmap -> recursive(%d)", // handle, swap->mapCount()); return swap->map(); // increase map count to 2... } // more complicated case: unmapped swapfile if (m_unmapped_swap.contains(handle)) { // map it into memory Kwave::SwapFile *swap = m_unmapped_swap[handle]; Q_ASSERT(!swap->mapCount()); void *mapped = swap->map(); if (!mapped) { qDebug("Kwave::MemoryManager[%9d] - mmap FAILED", handle); // maybe address space is already full wil already cached // mapped swap files -> kick out the last one and try again while (!mapped && !m_cached_swap.isEmpty()) { Kwave::Handle h = m_cached_swap.keys().last(); unmapFromCache(h); mapped = swap->map(); qDebug("Kwave::MemoryManager[%9d] - retry: %p", handle, mapped); } if (!mapped) return Q_NULLPTR; } // remember that we have mapped it, move the entry from the // "unmapped_swap" to the "mapped_swap" list m_unmapped_swap.remove(handle); m_mapped_swap.insert(handle, swap); #ifdef DEBUG_MEMORY m_stats.swap.unmapped.handles--; m_stats.swap.unmapped.bytes -= swap->size(); m_stats.swap.mapped.handles++; m_stats.swap.mapped.bytes += swap->size(); #endif /* DEBUG_MEMORY */ // qDebug("Kwave::MemoryManager[%9d] - mmap -> new mapping", handle); return mapped; } else { Q_ASSERT(m_unmapped_swap.contains(handle)); } // nothing known about this object !? return Q_NULLPTR; } //*************************************************************************** void Kwave::MemoryManager::unmapFromCache(Kwave::Handle handle) { if (m_cached_swap.contains(handle)) { // qDebug("Kwave::MemoryManager[%9d] - unmapFromCache", handle); Kwave::SwapFile *swap = m_cached_swap[handle]; Q_ASSERT(swap->mapCount() == 1); swap->unmap(); Q_ASSERT(!swap->mapCount()); m_cached_swap.remove(handle); m_unmapped_swap.insert(handle, swap); #ifdef DEBUG_MEMORY m_stats.swap.cached.handles--; m_stats.swap.cached.bytes -= swap->size(); m_stats.swap.unmapped.handles++; m_stats.swap.unmapped.bytes += swap->size(); #endif /* DEBUG_MEMORY */ } dump("unmap"); } //*************************************************************************** void Kwave::MemoryManager::unmap(Kwave::Handle handle) { QMutexLocker lock(&m_lock); // simple case: physical memory does not really need to be unmapped if (m_physical.contains(handle)) { // qDebug("Kwave::MemoryManager[%9d] - unmap -> physical", handle); Q_ASSERT(m_physical[handle].m_mapcount); if (m_physical[handle].m_mapcount) m_physical[handle].m_mapcount--; return; } // qDebug("Kwave::MemoryManager[%9d] - unmap swap", handle); // just to be sure: should also not be in cache! Q_ASSERT(!m_cached_swap.contains(handle)); unmapFromCache(handle); // unmapped swapfile: already unmapped !? if (m_unmapped_swap.contains(handle)) { Q_ASSERT(!m_unmapped_swap.contains(handle)); return; // nothing to do } // must be a mapped swapfile: move it into the cache Q_ASSERT(m_mapped_swap.contains(handle)); if (m_mapped_swap.contains(handle)) { Kwave::SwapFile *swap = m_mapped_swap[handle]; Q_ASSERT(swap->mapCount()); if (swap->mapCount() > 1) { // only unmap and internally reduce the map count swap->unmap(); // qDebug("Kwave::MemoryManager[%9d] - unmap -> recursive(%d)", // handle, swap->mapCount()); } else if (swap->mapCount() == 1) { // move to cache instead of really unmapping // make room in the cache if necessary while (m_cached_swap.count() >= CACHE_SIZE) { unmapFromCache(m_cached_swap.keys().first()); } // move it into the swap file cache m_mapped_swap.remove(handle); m_cached_swap.insert(handle, swap); #ifdef DEBUG_MEMORY m_stats.swap.mapped.handles--; m_stats.swap.mapped.bytes -= swap->size(); m_stats.swap.cached.handles++; m_stats.swap.cached.bytes += swap->size(); #endif /* DEBUG_MEMORY */ // qDebug("Kwave::MemoryManager[%9d] - unmap -> moved to cache", // handle); } } } //*************************************************************************** int Kwave::MemoryManager::readFrom(Kwave::Handle handle, unsigned int offset, void *buffer, unsigned int length) { QMutexLocker lock(&m_lock); if (!handle) return 0; // try to convert to physical RAM tryToMakePhysical(handle); // simple case: physical memory -> memcpy(...) if (m_physical.contains(handle)) { // qDebug("Kwave::MemoryManager[%9d] - readFrom -> physical", handle); char *data = reinterpret_cast(m_physical[handle].m_data); MEMCPY(buffer, data + offset, length); return length; } // no physical mem -> must be a swapfile // still in the cache and mapped -> memcpy(...) if (m_cached_swap.contains(handle)) { Kwave::SwapFile *swap = m_cached_swap[handle]; Q_ASSERT(swap->mapCount() == 1); char *data = reinterpret_cast(swap->address()); Q_ASSERT(data); if (!data) return 0; MEMCPY(buffer, data + offset, length); // qDebug("Kwave::MemoryManager[%9d] - readFrom -> cached swap", handle); return length; } // currently mmapped -> memcpy(...) if (m_mapped_swap.contains(handle)) { Kwave::SwapFile *swap = m_mapped_swap[handle]; Q_ASSERT(swap->mapCount() >= 1); char *data = reinterpret_cast(swap->address()); Q_ASSERT(data); if (!data) return 0; MEMCPY(buffer, data + offset, length); // qDebug("Kwave::MemoryManager[%9d] - readFrom -> mapped swap", handle); return length; } // now it must be in unmapped swap -> read(...) Q_ASSERT(m_unmapped_swap.contains(handle)); if (m_unmapped_swap.contains(handle)) { // qDebug("Kwave::MemoryManager[%9d] - readFrom -> unmapped swap", handle); Kwave::SwapFile *swap = m_unmapped_swap[handle]; length = swap->read(offset, buffer, length); return length; } return 0; } //*************************************************************************** int Kwave::MemoryManager::writeTo(Kwave::Handle handle, unsigned int offset, const void *buffer, unsigned int length) { QMutexLocker lock(&m_lock); if (!handle) return 0; // try to convert to physical RAM tryToMakePhysical(handle); // simple case: memcpy to physical memory if (m_physical.contains(handle)) { physical_memory_t &mem = m_physical[handle]; // qDebug("Kwave::MemoryManager[%9d] - writeTo -> physical", handle); char *data = reinterpret_cast(mem.m_data); Q_ASSERT(length <= mem.m_size); Q_ASSERT(offset < mem.m_size); Q_ASSERT(offset + length <= mem.m_size); MEMCPY(data + offset, buffer, length); return length; } // make sure it's not mmapped unmapFromCache(handle); // writing to mapped swap is not allowed Q_ASSERT(!m_mapped_swap.contains(handle)); if (m_mapped_swap.contains(handle)) { return 0; } // now it must be in unmapped swap Q_ASSERT(m_unmapped_swap.contains(handle)); if (m_unmapped_swap.contains(handle)) { // qDebug("Kwave::MemoryManager[%9d] - writeTo -> unmapped swap", handle); Kwave::SwapFile *swap = m_unmapped_swap[handle]; swap->write(offset, buffer, length); return length; } return 0; } //*************************************************************************** void Kwave::MemoryManager::dump(const char *function) { #if 0 quint64 v_used = virtualUsed(); quint64 p_used = physicalUsed(); qDebug("------- %s -------", function); foreach (const Kwave::Handle &handle, m_physical.keys()) qDebug(" P[%5u]: %5u", static_cast(handle), m_physical[handle].m_size >> 20); unsigned int m = 0; foreach (const Kwave::Handle &handle, m_mapped_swap.keys()) { m += m_mapped_swap[handle]->size() >> 20; qDebug(" M[%5u]: %5u", static_cast(handle), m_mapped_swap[handle]->size() >> 20); } unsigned int c = 0; foreach (const Kwave::Handle &handle, m_cached_swap.keys()) { c += m_cached_swap[handle]->size() >> 20; qDebug(" C[%5u]: %5u", static_cast(handle), m_cached_swap[handle]->size() >> 20); } unsigned int u = 0; foreach (const Kwave::Handle &handle, m_unmapped_swap.keys()) { u += m_unmapped_swap[handle]->size() >> 20; qDebug(" U[%5u]: %5u", static_cast(handle), m_unmapped_swap[handle]->size() >> 20); } qDebug("physical: %5llu MB, virtual: %5llu MB [m:%5u, c:%5u, u:%5u]", p_used, v_used, m, c, u); #endif #ifdef DEBUG_MEMORY qDebug("------- %s -------", function); qDebug("physical: %12llu, %12llu / %12llu (%12llu : %12llu)", m_stats.physical.handles, m_stats.physical.bytes, m_stats.physical.limit, m_stats.physical.allocs, m_stats.physical.frees); qDebug("mapped swap: %12llu, %12llu / %12llu (%12llu : %12llu)", m_stats.swap.mapped.handles, m_stats.swap.mapped.bytes, m_stats.swap.limit, m_stats.swap.allocs, m_stats.swap.frees); qDebug("cached: %12llu, %12llu", m_stats.swap.cached.handles, m_stats.swap.cached.bytes); qDebug("unmapped: %12llu, %12llu", m_stats.swap.unmapped.handles, m_stats.swap.unmapped.bytes); qDebug("-----------------------------------------------------------------"); #else /* DEBUG_MEMORY */ Q_UNUSED(function); #endif /* DEBUG_MEMORY */ } //*************************************************************************** //*************************************************************************** diff --git a/libkwave/Sample.h b/libkwave/Sample.h index f2a5e4a9..2c85b59b 100644 --- a/libkwave/Sample.h +++ b/libkwave/Sample.h @@ -1,89 +1,89 @@ /*************************************************************************** Sample.h - definition of the sample type ------------------- begin : Feb 09 2001 copyright : (C) 2001 by Thomas Eschenbacher email : Thomas Eschenbacher ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef SAMPLE_H #define SAMPLE_H //*************************************************************************** #include + #include -#include /** use an unsigned integer for sample offset/count calculations */ typedef quint64 sample_index_t; /** the highest possible sample index */ #define SAMPLE_INDEX_MAX ( std::numeric_limits::max() ) /** * Currently a "sample" is defined as a 32 bit integer * with 24 significant bits */ typedef qint32 sample_t; /** native data type used for storing a sample_t */ typedef qint32 sample_storage_t; /** number of significant bits per sample */ #define SAMPLE_BITS 24 /** number of bits used for storing samples in integer representation */ #define SAMPLE_STORAGE_BITS (sizeof(sample_storage_t) * 8) /** lowest sample value */ #define SAMPLE_MIN (-(1 << (SAMPLE_BITS - 1)) + 1) /** highest sample value */ #define SAMPLE_MAX (+(1 << (SAMPLE_BITS - 1)) - 1) /** * Simple conversion from float to sample_t */ static inline sample_t float2sample(const float f) { return static_cast( f * static_cast(1 << (SAMPLE_BITS - 1))); } /** * Simple conversion from sample_t to float */ static inline float sample2float(const sample_t s) { return static_cast( static_cast(s) / static_cast(1 << (SAMPLE_BITS - 1))); } /** * Simple conversion from sample_t to double */ static inline double sample2double(const sample_t s) { return static_cast( static_cast(s) / static_cast(1 << (SAMPLE_BITS - 1))); } /** * Simple conversion from double to sample_t */ static inline sample_t double2sample(const double f) { return static_cast( f * static_cast(1 << (SAMPLE_BITS - 1))); } #endif /* SAMPLE_H */ //*************************************************************************** //*************************************************************************** diff --git a/libkwave/SampleEncoderLinear.cpp b/libkwave/SampleEncoderLinear.cpp index e455355b..3ded3c6b 100644 --- a/libkwave/SampleEncoderLinear.cpp +++ b/libkwave/SampleEncoderLinear.cpp @@ -1,195 +1,192 @@ /************************************************************************* SampleEncoderLinear.cpp - encoder for all non-compressed linear formats ------------------- begin : Tue Apr 18 2006 copyright : (C) 2006 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "config.h" -#include -#include - #include #include "libkwave/Sample.h" #include "libkwave/SampleEncoderLinear.h" #include "libkwave/SampleFormat.h" #include "libkwave/Utils.h" //*************************************************************************** static void encode_NULL(const sample_t *src, quint8 *dst, unsigned int count) { (void)src; (void)dst; (void)count; // qWarning("call to encode_NULL"); } //*************************************************************************** /** * Template for encoding a buffer with linear samples. The tricky part is * done in the compiler which optimizes away all unused parts of current * variant and does nice loop optimizing! * @param src array with samples in Kwave's format * @param dst array that receives the raw data * @param count the number of samples to be encoded */ template void encode_linear(const sample_t *src, quint8 *dst, unsigned int count) { for ( ; count; --count) { // read from source buffer sample_t s = *(src++); // convert to unsigned if necessary if (!is_signed) - s += 1 << (SAMPLE_BITS-1); + s += 1 << (SAMPLE_BITS - 1); // shrink 18/20 bits and similar down, otherwise it does not work // with ALSA for some dubious reason !? if (bits == 20) s >>= 4; if (bits == 18) // don't ask me why... !!!??? s >>= 6; if (is_little_endian) { // little endian if (bits > 24) *(dst++) = 0x00; if (bits > 16) *(dst++) = static_cast(s & 0xFF); if (bits > 8) *(dst++) = static_cast(s >> 8); if (bits >= 8) *(dst++) = static_cast(s >> 16); } else { // big endian if (bits >= 8) *(dst++) = static_cast(s >> 16); if (bits > 8) *(dst++) = static_cast(s >> 8); if (bits > 16) *(dst++) = static_cast(s & 0xFF); if (bits > 24) *(dst++) = 0x00; } } } //*************************************************************************** #define MAKE_ENCODER(bits) \ if (sample_format != Kwave::SampleFormat::Unsigned) { \ if (endianness != Kwave::BigEndian) { \ m_encoder = encode_linear; \ } else { \ m_encoder = encode_linear; \ } \ } else { \ if (endianness != Kwave::BigEndian) { \ m_encoder = encode_linear; \ } else { \ m_encoder = encode_linear; \ } \ } //*************************************************************************** Kwave::SampleEncoderLinear::SampleEncoderLinear( Kwave::SampleFormat::Format sample_format, unsigned int bits_per_sample, Kwave::byte_order_t endianness ) :SampleEncoder(), m_bytes_per_sample((bits_per_sample + 7) >> 3), m_encoder(encode_NULL) { // sanity checks: we support only signed/unsigned and big/little endian Q_ASSERT((sample_format == Kwave::SampleFormat::Signed) || (sample_format == Kwave::SampleFormat::Unsigned)); if ((sample_format != Kwave::SampleFormat::Signed) && (sample_format != Kwave::SampleFormat::Unsigned)) return; // allow unknown endianness only with 8 bits Q_ASSERT((endianness != Kwave::UnknownEndian) || (m_bytes_per_sample == 1)); if ( (endianness == Kwave::UnknownEndian) && (m_bytes_per_sample != 1) ) return; // map cpu endianness to little or big #if Q_BYTE_ORDER == Q_BIG_ENDIAN if (endianness == Kwave::CpuEndian) endianness = Kwave::BigEndian; #else if (endianness == Kwave::CpuEndian) endianness = Kwave::LittleEndian; #endif // qDebug("SampleEncoderLinear::SampleEncoderLinear(fmt=%s, " // "%u bit [%u bytes], endian=%s)", // (sample_format == Kwave::SampleFormat::Signed) ? // "signed" : "unsigned", // bits_per_sample, m_bytes_per_sample, // (endianness == Kwave::BigEndian) ? "BE" : "LE"); switch (bits_per_sample) { case 8: MAKE_ENCODER(8); break; case 16: MAKE_ENCODER(16); break; case 18: MAKE_ENCODER(18); break; case 20: MAKE_ENCODER(20); break; case 24: MAKE_ENCODER(24); break; case 32: MAKE_ENCODER(32); break; } Q_ASSERT(m_encoder != encode_NULL); } //*************************************************************************** Kwave::SampleEncoderLinear::~SampleEncoderLinear() { } //*************************************************************************** void Kwave::SampleEncoderLinear::encode(const Kwave::SampleArray &samples, unsigned int count, QByteArray &raw_data) { Q_ASSERT(m_encoder); if (!m_encoder) return; Q_ASSERT(count * m_bytes_per_sample <= Kwave::toUint(raw_data.size())); if (count * m_bytes_per_sample > Kwave::toUint(raw_data.size())) return; const sample_t *src = samples.constData(); quint8 *dst = reinterpret_cast(raw_data.data()); m_encoder(src, dst, count); } //*************************************************************************** unsigned int Kwave::SampleEncoderLinear::rawBytesPerSample() { return m_bytes_per_sample; } //*************************************************************************** //*************************************************************************** diff --git a/libkwave/SwapFile.h b/libkwave/SwapFile.h index 9ec73ca1..8af6f186 100644 --- a/libkwave/SwapFile.h +++ b/libkwave/SwapFile.h @@ -1,146 +1,147 @@ /*************************************************************************** SwapFile.h - Provides virtual memory in a swap file ------------------- begin : Fri Aug 17 2001 copyright : (C) 2000 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef SWAP_FILE_H #define SWAP_FILE_H #include "config.h" -#include // for size_t + +#include // for size_t #include #include class QString; namespace Kwave { class SwapFile { public: /** * Constructor * @param name full path to the swap file, actually a template * that must contain 6 "X" characters at the end! */ explicit SwapFile(const QString &name); /** Destructor */ virtual ~SwapFile(); /** * Allocates virtual memory by creating an empty swap file. * Must be mapped into memory before used. * * @param size number of bytes to allocate * @return true if succeeded, false if failed */ bool allocate(size_t size); /** * Returns the address of the allocated memory or 0 if * nothing has been allocated. */ inline void *address() const { return m_address; } /** * Returns the size of the allocated memory or 0 if * nothing has been allocated. */ inline size_t size() const { return m_size; } /** returns the map count */ inline int mapCount() const { return m_map_count; } /** * Returns the size of one storage unit in bytes */ inline size_t pagesize() const { return m_pagesize; } /** * Resizes the allocated swap file. * @param size the new size * @return true if successful or false if failed */ bool resize(size_t size); /** * Map the memory and return the physical address. * * @return pointer to the mapped area or null if failed */ void *map(); /** * Unmap a memory area, previously mapped with map() * * @return current reference count */ int unmap(); /** * Read bytes into a buffer * * @param offset offset within the file [bytes] * @param buffer pointer to a buffer that is to be filled * @param length number of bytes to read * @return number of read bytes or < 0 if failed */ int read(unsigned int offset, void *buffer, unsigned int length); /** * Write bytes from a buffer * * @param offset offset within the file [bytes] * @param buffer pointer to a buffer with data * @param length number of bytes to write * @return number of written bytes or < 0 if failed */ int write(unsigned int offset, const void *buffer, unsigned int length); private: /** * Frees the allocated memory by unmapping and deleting * the swap file. */ void close(); private: /** file used for swapping */ QTemporaryFile m_file; /** address of the allocated virtual memory or 0 */ void *m_address; /** number of allocated bytes or 0 */ size_t m_size; /** size of one storage unit */ size_t m_pagesize; /** reference count for mmap [0...N] */ int m_map_count; }; } #endif /* SWAP_FILE_H */ //*************************************************************************** //*************************************************************************** diff --git a/plugins/pitch_shift/PitchShiftFilter.cpp b/plugins/pitch_shift/PitchShiftFilter.cpp index d6e53457..6d145234 100644 --- a/plugins/pitch_shift/PitchShiftFilter.cpp +++ b/plugins/pitch_shift/PitchShiftFilter.cpp @@ -1,214 +1,214 @@ /*************************************************************************** PitchShiftFilter.cpp - filter for modifying the "pitch_shift" ------------------- begin : Wed Nov 28 2007 copyright : (C) 2007 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de based on synth_pitch_shift_impl.cc from the aRts project copyright (C) 2000 Jeff Tranter (C) 1999 Stefan Westerfeld ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "config.h" -#include + #include #include "libkwave/Sample.h" #include "libkwave/Utils.h" #include "PitchShiftFilter.h" //*************************************************************************** Kwave::PitchShiftFilter::PitchShiftFilter() :Kwave::SampleSource(Q_NULLPTR), m_buffer(blockSize()), m_speed(1.0), m_frequency(0.5), m_dbuffer(), m_lfopos(0), m_b1pos(0), m_b2pos(0), m_b1inc(0), m_b2inc(0), m_b1reset(false), m_b2reset(false), m_dbpos(0) { initFilter(); } //*************************************************************************** Kwave::PitchShiftFilter::~PitchShiftFilter() { } //*************************************************************************** void Kwave::PitchShiftFilter::goOn() { emit output(m_buffer); } //*************************************************************************** void Kwave::PitchShiftFilter::initFilter() { m_dbuffer.resize(MAXDELAY); for (m_dbpos = 0; m_dbpos < MAXDELAY; m_dbpos++) m_dbuffer[m_dbpos] = 0; m_dbpos = 0; m_lfopos = 0; if (m_speed <= float(1.0)) { m_b1pos = m_b2pos = 0.0; m_b1inc = m_b2inc = 1.0f - m_speed; } else { /* not yet sure what would be a nice initialization here? */ m_b1pos = m_b2pos = 0.0; m_b1inc = m_b2inc = 0.0; } } //*************************************************************************** void Kwave::PitchShiftFilter::input(Kwave::SampleArray data) { const Kwave::SampleArray &in = data; Q_ASSERT(Kwave::toInt(in.size()) <= m_dbuffer.size()); bool ok = m_buffer.resize(in.size()); Q_ASSERT(ok); Q_UNUSED(ok); const float pi2 = 2 * float(M_PI); const float lfoposinc = static_cast(m_frequency); for (unsigned int pos = 0; pos < m_buffer.size(); pos++) { /* * fill delay buffer with the input signal */ m_dbuffer[m_dbpos] = sample2float(in[pos]); m_lfopos += lfoposinc; m_lfopos -= floorf(m_lfopos); if (m_lfopos < float(0.25)) { m_b1reset = m_b2reset = false; } /* * _speed < 1.0 (downpitching) * * start with current sample and increase delay slowly * * _speed > 1.0 (uppitching) * * start with a sample from long ago and slowly decrease delay */ if (!m_b1reset && m_lfopos > float(0.25)) { if (m_speed <= float(1.0)) { m_b1pos = 0; m_b1inc = 1.0f - m_speed; } else { m_b1inc = 1.0f - m_speed; m_b1pos = 10.0f + ((- m_b1inc) * (1.0f / lfoposinc)); /* 10+ are not strictly necessary */ } m_b1reset = true; } if (!m_b2reset && (m_lfopos > 0.75f)) { if (m_speed <= float(1.0)) { m_b2pos = 0; m_b2inc = 1.0f - m_speed; } else{ m_b2inc = 1.0f - m_speed; m_b2pos = 10.0f + ((-m_b2inc) * (1.0f / lfoposinc)); /* 10+ are not strictly necessary */ } m_b2reset = true; } m_b1pos += m_b1inc; m_b2pos += m_b2inc; int position, position1; float error, int_pos; /* * Interpolate value from buffer position 1 */ error = modff(m_b1pos, &int_pos); position = m_dbpos - Kwave::toInt(int_pos); if (position < 0) position += MAXDELAY; position1 = position - 1; if (position1 < 0) position1 += MAXDELAY; const float b1value = m_dbuffer[position] * (1 - error) + m_dbuffer[position1] * error; /* * Interpolate value from buffer position 2 */ error = modff(m_b2pos,&int_pos); position = m_dbpos - Kwave::toInt(int_pos); if (position < 0) position += MAXDELAY; position1 = position-1; if ( position1 < 0) position1 += MAXDELAY; const float b2value = m_dbuffer[position] * (1 - error) + m_dbuffer[position1] * error; /* * Calculate output signal from these two buffers */ const float lfo = (sinf(pi2 * m_lfopos) + 1.0f) / 2.0f; /* position sin lfo variable *------------------------------------------------------------------ * lfo value: 0.25 1 1 => buffer 2 is used * 0.75 -1 0 => buffer 1 is used */ m_buffer[pos] = float2sample(b1value * (1.0f - lfo) + b2value * lfo); /* * increment delay buffer position */ m_dbpos++; if (m_dbpos == MAXDELAY) m_dbpos = 0; } } //*************************************************************************** void Kwave::PitchShiftFilter::setSpeed(const QVariant speed) { float new_speed = QVariant(speed).toFloat(); if (qFuzzyCompare(new_speed, m_speed)) return; // nothing to do m_speed = new_speed; initFilter(); } //*************************************************************************** void Kwave::PitchShiftFilter::setFrequency(const QVariant freq) { float new_freq = QVariant(freq).toFloat(); if (qFuzzyCompare(new_freq, m_frequency)) return; // nothing to do m_frequency = new_freq; initFilter(); } //*************************************************************************** //*************************************************************************** diff --git a/plugins/playback/PlayBack-OSS.cpp b/plugins/playback/PlayBack-OSS.cpp index 40964cce..8f821bab 100644 --- a/plugins/playback/PlayBack-OSS.cpp +++ b/plugins/playback/PlayBack-OSS.cpp @@ -1,624 +1,624 @@ /*************************************************************************** PlayBack-OSS.cpp - playback device for standard linux OSS ------------------- begin : Sat May 19 2001 copyright : (C) 2001 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "config.h" #ifdef HAVE_OSS_SUPPORT #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include "libkwave/ByteOrder.h" #include "libkwave/Compression.h" #include "libkwave/SampleEncoderLinear.h" #include "libkwave/String.h" #include "libkwave/Utils.h" #include "libkwave/memcpy.h" #include "PlayBack-OSS.h" /** use at least 2^8 = 256 bytes for playback buffer !!! */ #define MIN_PLAYBACK_BUFFER 8 /** use at most 2^16 = 65536 bytes for playback buffer !!! */ #define MAX_PLAYBACK_BUFFER 16 /** highest available number of channels */ #define MAX_CHANNELS 7 // Linux 2.6.24 and above's OSS emulation supports 24 and 32 bit formats // but did not declare them in #ifndef AFMT_S24_LE #define AFMT_S24_LE 0x00008000 #endif #ifndef AFMT_S24_BE #define AFMT_S24_BE 0x00010000 #endif #ifndef AFMT_S32_LE #define AFMT_S32_LE 0x00001000 #endif #ifndef AFMT_S32_BE #define AFMT_S32_BE 0x00002000 #endif #ifndef SNDCTL_DSP_SPEED #define SNDCTL_DSP_SPEED SOUND_PCM_WRITE_RATE #endif #ifndef SNDCTL_DSP_CHANNELS #define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS #endif #ifndef SOUND_PCM_SETFMT #define SOUND_PCM_SETFMT SOUND_PCM_WRITE_BITS #endif #ifndef SNDCTL_DSP_SETFMT #define SNDCTL_DSP_SETFMT SOUND_PCM_SETFMT #endif //*************************************************************************** Kwave::PlayBackOSS::PlayBackOSS() :Kwave::PlayBackDevice(), m_device_name(), m_handle(-1), m_rate(0), m_channels(0), m_bits(0), m_bufbase(0), m_buffer(), m_raw_buffer(), m_buffer_size(0), m_buffer_used(0), m_encoder(Q_NULLPTR), m_oss_version(-1) { } //*************************************************************************** Kwave::PlayBackOSS::~PlayBackOSS() { close(); } //*************************************************************************** QString Kwave::PlayBackOSS::open(const QString &device, double rate, unsigned int channels, unsigned int bits, unsigned int bufbase) { qDebug("PlayBackOSS::open(device=%s,rate=%0.1f,channels=%u," "bits=%u, bufbase=%u)", DBG(device.split(_("|")).at(0)), rate, channels, bits, bufbase); m_device_name = device; m_rate = rate; m_channels = channels; m_bits = bits; m_bufbase = bufbase; m_buffer_size = 0; m_buffer_used = 0; m_handle = 0; // prepeare for playback by opening the sound device // and initializing with the proper settings m_handle = ::open(m_device_name.toLocal8Bit(), O_WRONLY | O_NONBLOCK); if (m_handle == -1) { QString reason; switch (errno) { case ENOENT: case ENODEV: case ENXIO: case EIO: reason = i18n("I/O error. Maybe the driver\n"\ "is not present in your kernel or it is not\n"\ "properly configured."); break; case EBUSY: reason = i18n( "The device is busy. Maybe some other application is\n"\ "currently using it. Please try again later.\n"\ "(Hint: you might find out the name and process ID of\n"\ "the program by calling: \"fuser -v %1\"\n"\ "on the command line.)", m_device_name.section(QLatin1Char('|'), 0, 0)); break; default: reason = QString::fromLocal8Bit(strerror(errno)); } return reason; } // the device was opened in non-blocking mode to detect if it is // busy or not - but from now on we need blocking mode again ::fcntl(m_handle, F_SETFL, fcntl(m_handle, F_GETFL) & ~O_NONBLOCK); if (fcntl(m_handle, F_GETFL) & O_NONBLOCK) { // resetting O:NONBLOCK failed return i18n("The device '%1' cannot be opened "\ "in the correct mode.", m_device_name.section(QLatin1Char('|'), 0, 0)); } // query OSS driver version m_oss_version = 0x030000; #ifdef OSS_GETVERSION ioctl(m_handle, OSS_GETVERSION, &m_oss_version); #endif int format; switch (m_bits) { case 8: format = AFMT_U8; break; case 24: format = AFMT_S24_LE; break; case 32: format = AFMT_S32_LE; break; default: format = AFMT_S16_LE; } // number of bits per sample int oldformat = format; if ((ioctl(m_handle, SNDCTL_DSP_SETFMT, &format) == -1) || (format != oldformat)) { return i18n("%1 bits per sample are not supported", m_bits); } // channels selection if ((ioctl(m_handle, SNDCTL_DSP_CHANNELS, &m_channels) == -1) || (format != oldformat)) { return i18n("%1 channels playback is not supported", m_channels); } // playback rate, we allow up to 10% variation int int_rate = Kwave::toInt(m_rate); if ((ioctl(m_handle, SNDCTL_DSP_SPEED, &int_rate) == -1) || (int_rate < 0.9 * m_rate) || (int_rate > 1.1 * m_rate)) { return i18n("Playback rate %1 Hz is not supported", int_rate); } m_rate = int_rate; // buffer size Q_ASSERT(bufbase >= MIN_PLAYBACK_BUFFER); Q_ASSERT(bufbase <= MAX_PLAYBACK_BUFFER); if (bufbase < MIN_PLAYBACK_BUFFER) bufbase = MIN_PLAYBACK_BUFFER; if (bufbase > MAX_PLAYBACK_BUFFER) bufbase = MAX_PLAYBACK_BUFFER; if (ioctl(m_handle, SNDCTL_DSP_SETFRAGMENT, &bufbase) == -1) { return i18n("Unusable buffer size: %1", 1 << bufbase); } // get the real buffer size in bytes ioctl(m_handle, SNDCTL_DSP_GETBLKSIZE, &m_buffer_size); // create the sample encoder // we assume that OSS is always little endian if (m_encoder) delete m_encoder; switch (m_bits) { case 8: m_encoder = new(std::nothrow) Kwave::SampleEncoderLinear( Kwave::SampleFormat::Unsigned, 8, Kwave::LittleEndian); break; case 24: if (m_oss_version >= 0x040000) { m_encoder = new(std::nothrow) Kwave::SampleEncoderLinear( Kwave::SampleFormat::Signed, 24, Kwave::LittleEndian); break; } // else: /* FALLTHROUGH */ case 32: if (m_oss_version >= 0x040000) { m_encoder = new(std::nothrow) Kwave::SampleEncoderLinear( Kwave::SampleFormat::Signed, 32, Kwave::LittleEndian); break; } // else: /* FALLTHROUGH */ default: m_encoder = new(std::nothrow) Kwave::SampleEncoderLinear( Kwave::SampleFormat::Signed, 16, Kwave::LittleEndian); break; } Q_ASSERT(m_encoder); if (!m_encoder) return i18n("Out of memory"); // resize the raw buffer m_raw_buffer.resize(m_buffer_size); // resize our buffer (size in samples) and reset it m_buffer_size /= m_encoder->rawBytesPerSample(); if (!m_buffer.resize(m_buffer_size)) return i18n("Out of memory"); return QString(); } //*************************************************************************** int Kwave::PlayBackOSS::write(const Kwave::SampleArray &samples) { Q_ASSERT (m_buffer_used <= m_buffer_size); if (m_buffer_used > m_buffer_size) { qWarning("PlayBackOSS::write(): buffer overflow ?!"); m_buffer_used = m_buffer_size; flush(); return -EIO; } // number of samples left in the buffer unsigned int remaining = samples.size(); unsigned int offset = 0; while (remaining) { unsigned int length = remaining; if (m_buffer_used + length > m_buffer_size) length = m_buffer_size - m_buffer_used; MEMCPY(&(m_buffer[m_buffer_used]), &(samples[offset]), length * sizeof(sample_t)); m_buffer_used += length; offset += length; remaining -= length; // write buffer to device if it has become full if (m_buffer_used >= m_buffer_size) flush(); } return 0; } //*************************************************************************** void Kwave::PlayBackOSS::flush() { if (!m_buffer_used || !m_encoder) return; // nothing to do // convert into byte stream unsigned int bytes = m_buffer_used * m_encoder->rawBytesPerSample(); m_encoder->encode(m_buffer, m_buffer_used, m_raw_buffer); ssize_t res = 0; if (m_handle) res = ::write(m_handle, m_raw_buffer.data(), bytes); if (res < 0) perror(__FUNCTION__); m_buffer_used = 0; } //*************************************************************************** int Kwave::PlayBackOSS::close() { flush(); // close the device handle if (m_handle) ::close(m_handle); // get rid of the old encoder if (m_encoder) delete m_encoder; m_encoder = Q_NULLPTR; return 0; } //*************************************************************************** static bool addIfExists(QStringList &list, const QString &name) { QFile file; if (name.contains(_("%1"))) { // test for the name without suffix first addIfExists(list, name.arg(_(""))); // loop over the list and try until a suffix does not exist for (unsigned int index=0; index < 64; index++) addIfExists(list, name.arg(index)); } else { // check a single name file.setFileName(name); if (!file.exists()) return false; if (!list.contains(name)) list.append(name); } return true; } //*************************************************************************** static void scanFiles(QStringList &list, const QString &dirname, const QString &mask) { QStringList files; QDir dir; dir.setPath(dirname); dir.setNameFilters(mask.split(QLatin1Char(' '))); dir.setFilter(QDir::Files | QDir::Writable | QDir::System); dir.setSorting(QDir::Name); files = dir.entryList(); for (QStringList::Iterator it=files.begin(); it != files.end(); ++it) { QString devicename = dirname + QDir::separator() + (*it); addIfExists(list, devicename); } } //*************************************************************************** static void scanDirectory(QStringList &list, const QString &dir) { scanFiles(list, dir, _("dsp*")); scanFiles(list, dir, _("*audio*")); scanFiles(list, dir, _("adsp*")); scanFiles(list, dir, _("dio*")); scanFiles(list, dir, _("pcm*")); } //*************************************************************************** QStringList Kwave::PlayBackOSS::supportedDevices() { QStringList list, dirlist; scanDirectory(list, _("/dev")); scanDirectory(list, _("/dev/snd")); scanDirectory(list, _("/dev/sound")); scanFiles(dirlist, _("/dev/oss"), _("[^.]*")); foreach (QString dir, dirlist) scanDirectory(list, dir); list.append(_("#EDIT#")); list.append(_("#SELECT#")); return list; } //*************************************************************************** QString Kwave::PlayBackOSS::fileFilter() { QString filter; if (filter.length()) filter += _("\n"); filter += _("dsp*|") + i18n("OSS playback device (dsp*)"); if (filter.length()) filter += _("\n"); filter += _("adsp*|") + i18n("ALSA playback device (adsp*)"); if (filter.length()) filter += _("\n"); filter += _("*|") + i18n("Any device (*)"); return filter; } //*************************************************************************** void Kwave::PlayBackOSS::format2mode(int format, int &compression, int &bits, Kwave::SampleFormat::Format &sample_format) const { switch (format) { case AFMT_MU_LAW: compression = Kwave::Compression::G711_ULAW; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_A_LAW: compression = Kwave::Compression::G711_ALAW; sample_format = Kwave::SampleFormat::Unsigned; bits = 16; break; case AFMT_IMA_ADPCM: compression = Kwave::Compression::MS_ADPCM; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_U8: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Unsigned; bits = 8; break; case AFMT_S16_LE: case AFMT_S16_BE: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_S8: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 8; break; case AFMT_U16_LE: case AFMT_U16_BE: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Unsigned; bits = 16; break; case AFMT_MPEG: compression = static_cast( Kwave::Compression::MPEG_LAYER_II); sample_format = Kwave::SampleFormat::Signed; bits = 16; break; #if 0 case AFMT_AC3: /* Dolby Digital AC3 */ compression = Kwave::SampleFormat::Unknown; sample_format = 0; bits = 16; break; #endif case AFMT_S24_LE: case AFMT_S24_BE: if (m_oss_version >= 0x040000) { compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 24; break; } /* FALLTHROUGH */ case AFMT_S32_LE: case AFMT_S32_BE: if (m_oss_version >= 0x040000) { compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 32; break; } /* FALLTHROUGH */ default: compression = -1; sample_format = Kwave::SampleFormat::Unknown; bits = -1; } } //*************************************************************************** int Kwave::PlayBackOSS::openDevice(const QString &device) { int fd = m_handle; // qDebug("PlayBackOSS::openDevice(%s)", device.local8Bit().data()); if (!device.length()) return -1; if (fd <= 0) { // open the device in case it's not already open fd = ::open(device.toLocal8Bit(), O_WRONLY | O_NONBLOCK); if (fd <= 0) { qWarning("PlayBackOSS::openDevice('%s') - failed, errno=%d (%s)", DBG(device), errno, strerror(errno)); } else { // we use blocking mode ::fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); // query OSS driver version m_oss_version = -1; #ifdef OSS_GETVERSION ioctl(fd, OSS_GETVERSION, &m_oss_version); #endif } } if (fd <= 0) { qWarning("PlayBackOSS::openDevice('%s') - failed, errno=%d (%s)", DBG(device), errno, strerror(errno)); } return fd; } //*************************************************************************** QList Kwave::PlayBackOSS::supportedBits(const QString &device) { QList bits; bits.clear(); int err = -1; int mask = AFMT_QUERY; int fd; // qDebug("PlayBackOSS::supportedBits(%s)", device.local8Bit().data()); fd = openDevice(device); if (fd >= 0) { err = ::ioctl(fd, SNDCTL_DSP_GETFMTS, &mask); if (err < 0) { qWarning("PlayBackOSS::supportedBits() - "\ "SNDCTL_DSP_GETFMTS failed, "\ "fd=%d, result=%d, error=%d (%s)", fd, err, errno, strerror(errno)); } } // close the device if *we* opened it if ((fd != m_handle) && (fd >= 0)) ::close(fd); if (err < 0) return bits; // mask out all modes that do not match the current compression const int compression = Kwave::Compression::NONE; for (unsigned int bit=0; bit < (sizeof(mask) << 3); bit++) { if (!(mask & (1 << bit))) continue; // format is supported, split into compression, bits, sample format int c, b; Kwave::SampleFormat::Format s; format2mode(1 << bit, c, b, s); if (b < 0) continue; // unknown -> skip // take the mode if compression matches and it is not already known if ((c == compression) && !(bits.contains(b))) { bits += b; } } return bits; } //*************************************************************************** int Kwave::PlayBackOSS::detectChannels(const QString &device, unsigned int &min, unsigned int &max) { int fd, t, err = -1; min = 0; max = 0; fd = openDevice(device); if (fd < 0) return -1; // find the smalles number of tracks, limit to MAX_CHANNELS for (t=1; t < MAX_CHANNELS; t++) { int real_tracks = t; err = ioctl(fd, SNDCTL_DSP_CHANNELS, &real_tracks); Q_ASSERT(real_tracks == t); if (err >= 0) { min = real_tracks; break; } } if (t >= MAX_CHANNELS) { // no minimum track number found :-o qWarning("no minimum track number found, err=%d", err); // close the device if *we* opened it if ((fd != m_handle) && (fd >= 0)) ::close(fd); return err; } // find the highest number of tracks, start from MAX_CHANNELS downwards for (t = MAX_CHANNELS; t >= Kwave::toInt(min); t--) { int real_tracks = t; err = ioctl(fd, SNDCTL_DSP_CHANNELS, &real_tracks); Q_ASSERT(real_tracks == t); if (err >= 0) { max = real_tracks; break; } } max = t; // qDebug("PlayBackOSS::detectTracks, min=%u, max=%u", min, max); // close the device if *we* opened it if ((fd != m_handle) && (fd >= 0)) ::close(fd); return 0; } #endif /* HAVE_OSS_SUPPORT */ //*************************************************************************** //*************************************************************************** diff --git a/plugins/record/Record-OSS.cpp b/plugins/record/Record-OSS.cpp index cfda1a30..18b75b9f 100644 --- a/plugins/record/Record-OSS.cpp +++ b/plugins/record/Record-OSS.cpp @@ -1,870 +1,870 @@ /************************************************************************* Record-OSS.cpp - device for audio recording via OSS ------------------- begin : Sun Jul 24 2005 copyright : (C) 2005 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "config.h" #ifdef HAVE_OSS_SUPPORT #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include "libkwave/Compression.h" #include "libkwave/SampleFormat.h" #include "libkwave/String.h" #include "libkwave/Utils.h" #include "Record-OSS.h" // Linux 2.6.24 and above's OSS emulation supports 24 and 32 bit formats // but did not declare them in #ifndef AFMT_S24_LE #define AFMT_S24_LE 0x00008000 #endif #ifndef AFMT_S24_BE #define AFMT_S24_BE 0x00010000 #endif #ifndef AFMT_S32_LE #define AFMT_S32_LE 0x00001000 #endif #ifndef AFMT_S32_BE #define AFMT_S32_BE 0x00002000 #endif #ifndef SNDCTL_DSP_SPEED #define SNDCTL_DSP_SPEED SOUND_PCM_WRITE_RATE #endif #ifndef SNDCTL_DSP_CHANNELS #define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS #endif #ifndef SOUND_PCM_SETFMT #define SOUND_PCM_SETFMT SOUND_PCM_WRITE_BITS #endif #ifndef SNDCTL_DSP_SETFMT #define SNDCTL_DSP_SETFMT SOUND_PCM_SETFMT #endif #define MAX_CHANNELS 2 /**< highest available number of channels */ //*************************************************************************** Kwave::RecordOSS::RecordOSS() :Kwave::RecordDevice(), m_fd(-1), m_rate(0), m_tracks(0), m_oss_version(-1) { } //*************************************************************************** Kwave::RecordOSS::~RecordOSS() { close(); } //*************************************************************************** QString Kwave::RecordOSS::open(const QString &dev) { // close the device if it is still open if (m_fd >= 0) close(); if (!dev.length()) return QString::number(EINVAL); // no device name // first of all: try to open the device itself int fd = ::open(dev.toLocal8Bit(), O_RDONLY | O_NONBLOCK); if (fd < 0) { qWarning("open failed, fd=%d, errno=%d (%s)", fd, errno, strerror(errno)); QString reason; switch (errno) { case ENOENT: case ENODEV: case ENXIO: case EIO: reason = QString::number(ENODEV); break; case EBUSY: reason = QString::number(EBUSY); break; default: reason = QString::fromLocal8Bit(strerror(errno)); break; } return reason; } // Query OSS driver version m_oss_version = 0x030000; #ifdef OSS_GETVERSION ioctl(fd, OSS_GETVERSION, &m_oss_version); #endif m_fd = fd; return QString(); } //*************************************************************************** int Kwave::RecordOSS::read(QByteArray &buffer, unsigned int offset) { fd_set rfds; struct timeval tv; int retval; int read_bytes = 0; unsigned int length = buffer.size(); // qDebug("RecordOSS::read(), offset=%d, length=%d", offset, length); Q_ASSERT(m_fd >= 0); Q_ASSERT(buffer.size()); Q_ASSERT(length); Q_ASSERT(offset < length); if (m_fd < 0) return -EBADF; // file not opened if (buffer.isEmpty()) return -EINVAL; // buffer is null pointer if (!length) return -EINVAL; if (offset >= length) return -EINVAL; length -= offset; #if 0 int blocksize = length; int err = ioctl(m_fd, SNDCTL_DSP_GETBLKSIZE, &blocksize); Q_ASSERT(!err); qDebug("blocksize = %u", blocksize); if (err) { blocksize = length; } blocksize = (127 << 16) + 6; err = ioctl(m_fd, SNDCTL_DSP_SETFRAGMENT, &blocksize); #endif // determine the timeout for reading, use safety factor 2 int rate = Kwave::toInt(sampleRate()); if (rate < 1) rate = 1; unsigned int timeout = (length / rate) * 2; if (timeout < 2) timeout = 2; quint8 *buf = reinterpret_cast(buffer.data()) + offset; int mask = 0; retval = ioctl(m_fd, SNDCTL_DSP_SETTRIGGER, &mask); Q_ASSERT(!retval); mask = PCM_ENABLE_INPUT; retval = ioctl(m_fd, SNDCTL_DSP_SETTRIGGER, &mask); Q_ASSERT(!retval); while (length) { FD_ZERO(&rfds); FD_SET(m_fd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; retval = select(m_fd+1, &rfds, Q_NULLPTR, Q_NULLPTR, &tv); if (retval == -1) { if (errno == EINTR) return -errno; // return without warning qWarning("RecordOSS::read() - select() failed errno=%d (%s)", errno, strerror(errno)); return -errno; } else if (retval) { ssize_t res = ::read(m_fd, buf, length); if ((res == -1) && (errno == EINTR)) return -errno; // interrupted, return without warning if ((res == -1) && (errno == EAGAIN)) continue; if (res < 0) { qWarning("RecordOSS::read() - read error %d (%s)", errno, strerror(errno)); return read_bytes; } read_bytes += res; length -= res; buf += res; } else { printf("No data within 5 seconds.\n"); return -EIO; } } return read_bytes; } //*************************************************************************** int Kwave::RecordOSS::close() { if (m_fd < 0) return 0; // already closed ::close(m_fd); m_fd = -1; m_oss_version = -1; return 0; } //*************************************************************************** static bool addIfExists(QStringList &list, const QString &name) { QFile file; if (name.contains(_("%1"))) { // test for the name without suffix first addIfExists(list, name.arg(_(""))); // loop over the list and try until a suffix does not exist for (unsigned int index=0; index < 64; index++) addIfExists(list, name.arg(index)); } else { // check a single name file.setFileName(name); if (!file.exists()) return false; if (!list.contains(name)) list.append(name); } return true; } //*************************************************************************** static void scanFiles(QStringList &list, const QString &dirname, const QString &mask) { QStringList files; QDir dir; dir.setPath(dirname); dir.setNameFilters(mask.split(QLatin1Char(' '))); dir.setFilter(QDir::Files | QDir::Readable | QDir::System); dir.setSorting(QDir::Name); files = dir.entryList(); for (QStringList::Iterator it = files.begin(); it != files.end(); ++it) { QString devicename = dirname + QDir::separator() + (*it); addIfExists(list, devicename); } } //*************************************************************************** static void scanDirectory(QStringList &list, const QString &dir) { scanFiles(list, dir, _("*audio*")); scanFiles(list, dir, _("adsp*")); scanFiles(list, dir, _("dsp*")); scanFiles(list, dir, _("dio*")); scanFiles(list, dir, _("pcm*")); } //*************************************************************************** QStringList Kwave::RecordOSS::supportedDevices() { QStringList list, dirlist; scanDirectory(list, _("/dev")); scanDirectory(list, _("/dev/sound")); scanFiles(dirlist, _("/dev/oss"), _("[^.]*")); foreach(QString dir, dirlist) scanDirectory(list, dir); list.append(_("#EDIT#")); list.append(_("#SELECT#")); return list; } //*************************************************************************** QString Kwave::RecordOSS::fileFilter() { QString filter; if (filter.length()) filter += _("\n"); filter += _("audio*|") + i18n("OSS recording device (audio*)"); filter += _("dsp*|") + i18n("OSS recording device (dsp*)"); if (filter.length()) filter += _("\n"); filter += _("adsp*|") + i18n("ALSA recording device (adsp*)"); if (filter.length()) filter += _("\n"); filter += _("*|") + i18n("Any device (*)"); return filter; } //*************************************************************************** int Kwave::RecordOSS::detectTracks(unsigned int &min, unsigned int &max) { Q_ASSERT(m_fd >= 0); unsigned int t; int err = -1; // preset min = 0; max = 0; // find the smalles number of tracks, limit to MAX_CHANNELS for (t = 1; t < MAX_CHANNELS; t++) { int real_tracks = t; err = ioctl(m_fd, SNDCTL_DSP_CHANNELS, &real_tracks); if ((err >= 0) && (real_tracks == Kwave::toInt(t))) { min = real_tracks; break; } } if (t >= MAX_CHANNELS) { // no minimum track number found :-o qWarning("no minimum track number found, err=%d", err); min = 0; max = 0; return err; } // find the highest number of tracks, start from MAX_CHANNELS downwards max = min; for (t = MAX_CHANNELS; t >= min; t--) { int real_tracks = t; err = ioctl(m_fd, SNDCTL_DSP_CHANNELS, &real_tracks); if ((err >= 0) && (real_tracks == Kwave::toInt(t))) { max = real_tracks; break; } } m_tracks = max; qDebug("RecordOSS::detectTracks, min=%u, max=%u", min, max); return (max > 0) ? 0 : -1; } //*************************************************************************** int Kwave::RecordOSS::setTracks(unsigned int &tracks) { Q_ASSERT(m_fd >= 0); // set the number of tracks in the device (must already be opened) int t = tracks; int err = ioctl(m_fd, SNDCTL_DSP_CHANNELS, &t); if (err < 0) return err; m_tracks = t; // return the number of tracks if succeeded tracks = t; return 0; } //*************************************************************************** int Kwave::RecordOSS::tracks() { return m_tracks; } //*************************************************************************** QList Kwave::RecordOSS::detectSampleRates() { QList list; Q_ASSERT(m_fd >= 0); static const int known_rates[] = { 1000, // (just for testing) 2000, // (just for testing) 4000, // standard OSS 5125, // seen in Harmony driver (HP712, 715/new) 5510, // seen in AD1848 driver 5512, // seen in ES1370 driver 6215, // seen in ES188X driver 6615, // seen in Harmony driver (HP712, 715/new) 6620, // seen in AD1848 driver 7350, // seen in AWACS and Burgundy sound driver 8000, // standard OSS 8820, // seen in AWACS and Burgundy sound driver 9600, // seen in AD1848 driver 11025, // soundblaster 14700, // seen in AWACS and Burgundy sound driver 16000, // standard OSS 17640, // seen in AWACS and Burgundy sound driver 18900, // seen in Harmony driver (HP712, 715/new) 22050, // soundblaster 24000, // seen in NM256 driver 27428, // seen in Harmony driver (HP712, 715/new) 29400, // seen in AWACS and Burgundy sound driver 32000, // standard OSS 32768, // seen in CS4299 driver 33075, // seen in Harmony driver (HP712, 715/new) 37800, // seen in Harmony driver (HP712, 715/new) 44100, // soundblaster 48000, // AC97 64000, // AC97 88200, // seen in RME96XX driver 96000, // AC97 128000, // (just for testing) 176400, // Envy24ht 192000, // AC97 196000, // (just for testing) 200000, // Lynx2 256000 // (just for testing) }; // try all known sample rates for (unsigned int i=0; i < sizeof(known_rates)/sizeof(int); i++) { int rate = known_rates[i]; int err = ioctl(m_fd, SNDCTL_DSP_SPEED, &rate); if (err < 0) { // qDebug("RecordOSS::detectSampleRates(): " // "sample rate %d Hz not supported", known_rates[i]); continue; } // do not produce duplicates bool is_duplicate = false; foreach (const double &r, list) if (qFuzzyCompare(rate, r)) { is_duplicate = true; break; } if (is_duplicate) continue; // qDebug("found rate %d Hz", rate); list.append(rate); m_rate = rate; } return list; } //*************************************************************************** int Kwave::RecordOSS::setSampleRate(double &new_rate) { Q_ASSERT(m_fd >= 0); // OSS supports only integer rates int rate = Kwave::toInt(rint(new_rate)); // set the rate of the device (must already be opened) int err = ioctl(m_fd, SNDCTL_DSP_SPEED, &rate); if (err < 0) return err; m_rate = rate; // return the sample rate if succeeded new_rate = static_cast(rate); return 0; } //*************************************************************************** double Kwave::RecordOSS::sampleRate() { Q_ASSERT(m_fd >= 0); return m_rate; } //*************************************************************************** void Kwave::RecordOSS::format2mode(int format, Kwave::Compression::Type &compression, int &bits, Kwave::SampleFormat::Format &sample_format) { switch (format) { case AFMT_MU_LAW: compression = Kwave::Compression::G711_ULAW; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_A_LAW: compression = Kwave::Compression::G711_ALAW; sample_format = Kwave::SampleFormat::Unsigned; bits = 16; break; case AFMT_IMA_ADPCM: compression = Kwave::Compression::MS_ADPCM; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_U8: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Unsigned; bits = 8; break; case AFMT_S16_LE: /* FALLTHROUGH */ case AFMT_S16_BE: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_S8: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 8; break; case AFMT_U16_LE: /* FALLTHROUGH */ case AFMT_U16_BE: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Unsigned; bits = 16; break; case AFMT_MPEG: compression = Kwave::Compression::MPEG_LAYER_II; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; #if 0 case AFMT_AC3: /* Dolby Digital AC3 */ compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Unknown; bits = 16; break; #endif case AFMT_S24_LE: /* FALLTHROUGH */ case AFMT_S24_BE: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 24; break; case AFMT_S32_LE: /* FALLTHROUGH */ case AFMT_S32_BE: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 32; break; default: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Unknown; bits = -1; } } //*************************************************************************** int Kwave::RecordOSS::mode2format(Kwave::Compression::Type compression, int bits, Kwave::SampleFormat::Format sample_format) { // first level: compression if (compression == Kwave::Compression::G711_ULAW) return AFMT_MU_LAW; if (compression == Kwave::Compression::G711_ALAW) return AFMT_A_LAW; if (compression == Kwave::Compression::MS_ADPCM) return AFMT_IMA_ADPCM; if (compression == static_cast(Kwave::Compression::MPEG_LAYER_II)) return AFMT_MPEG; // non-compressed: switch by sample format if ((sample_format == Kwave::SampleFormat::Unsigned) && (bits == 8)) return AFMT_U8; if ((sample_format == Kwave::SampleFormat::Signed) && (bits == 8)) return AFMT_S8; // get supported modes, maybe one endianness is not supported int mask = 0; int err = ioctl(m_fd, SNDCTL_DSP_GETFMTS, &mask); if (err < 0) return bits; // unsigned 16 bit mode // note: we prefer the machine's native endianness if both are supported ! if ((sample_format == Kwave::SampleFormat::Unsigned) && (bits == 16)) { mask &= (AFMT_U16_LE | AFMT_U16_BE); if (mask != (AFMT_U16_LE | AFMT_U16_BE)) return mask; #if Q_BYTE_ORDER == Q_BIG_ENDIAN return AFMT_U16_BE; #else return AFMT_U16_LE; #endif } // signed 16 bit mode if ((sample_format == Kwave::SampleFormat::Signed) && (bits == 16)) { mask &= (AFMT_S16_LE | AFMT_S16_BE); if (mask != (AFMT_S16_LE | AFMT_S16_BE)) return mask; #if Q_BYTE_ORDER == Q_BIG_ENDIAN return AFMT_S16_BE; #else return AFMT_S16_LE; #endif } if ((sample_format == Kwave::SampleFormat::Signed) && (bits == 24)) { mask &= (AFMT_S24_LE | AFMT_S24_BE); if (mask != (AFMT_S24_LE | AFMT_S24_BE)) return mask; #if Q_BYTE_ORDER == Q_BIG_ENDIAN return AFMT_S24_BE; #else return AFMT_S24_LE; #endif } if ((sample_format == Kwave::SampleFormat::Signed) && (bits == 32)) { mask &= (AFMT_S32_LE | AFMT_S32_BE); if (mask != (AFMT_S32_LE | AFMT_S32_BE)) return mask; #if Q_BYTE_ORDER == Q_BIG_ENDIAN return AFMT_S32_BE; #else return AFMT_S32_LE; #endif } qWarning("RecordOSS: unknown format: sample_format=%d, bits=%d", static_cast(sample_format), bits); return 0; } //*************************************************************************** QList Kwave::RecordOSS::detectCompressions() { Q_ASSERT(m_fd >= 0); QList compressions; int err = 0; int mask = AFMT_QUERY; err = ioctl(m_fd, SNDCTL_DSP_GETFMTS, &mask); if (err < 0) return compressions; if (mask & AFMT_MPEG) compressions += Kwave::Compression::MPEG_LAYER_II; if (mask & AFMT_A_LAW) compressions += Kwave::Compression::G711_ALAW; if (mask & AFMT_MU_LAW) compressions += Kwave::Compression::G711_ULAW; if (mask & AFMT_IMA_ADPCM) compressions += Kwave::Compression::MS_ADPCM; if (mask & (AFMT_U16_LE | AFMT_U16_BE | AFMT_S16_LE | AFMT_S16_BE | AFMT_S24_LE | AFMT_S24_BE | AFMT_S32_LE | AFMT_S32_BE | AFMT_S8 | AFMT_U8)) compressions += Kwave::Compression::NONE; return compressions; } //*************************************************************************** int Kwave::RecordOSS::setCompression(Kwave::Compression::Type new_compression) { Q_ASSERT(m_fd >= 0); Kwave::Compression::Type compression; int bits, format = AFMT_QUERY; Kwave::SampleFormat::Format sample_format; // read back current format int err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &format); if (err < 0) return -1; format2mode(format, compression, bits, sample_format); // modify the compression compression = new_compression; // activate new format format = mode2format(compression, bits, sample_format); err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &format); if (err < 0) return -1; return 0; } //*************************************************************************** Kwave::Compression::Type Kwave::RecordOSS::compression() { Q_ASSERT(m_fd >= 0); int mask = AFMT_QUERY; int err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &mask); if (err < 0) return Kwave::Compression::NONE; Kwave::Compression::Type c; int b; Kwave::SampleFormat::Format s; format2mode(mask, c, b, s); return c; } //*************************************************************************** QList Kwave::RecordOSS::supportedBits() { Q_ASSERT(m_fd >= 0); QList bits; bits.clear(); int err = 0; int mask = AFMT_QUERY; err = ioctl(m_fd, SNDCTL_DSP_GETFMTS, &mask); if (err < 0) return bits; // mask out all modes that do not match the current compression const int compression = this->compression(); for (unsigned int bit=0; bit < (sizeof(mask) << 3); bit++) { if (!(mask & (1 << bit))) continue; // format is supported, split into compression, bits, sample format Kwave::Compression::Type c(Kwave::Compression::NONE); int b; Kwave::SampleFormat::Format s; format2mode(1 << bit, c, b, s); if (b < 0) continue; // unknown -> skip // take the mode if compression matches and it is not already known if ((c == compression) && !(bits.contains(b))) { bits += b; } } #if 0 if (mask | AFMT_AC3) qDebug("RecordOSS: your device supports AC3 which is not "\ "yet supported, sorry :-("); #endif return bits; } //*************************************************************************** int Kwave::RecordOSS::setBitsPerSample(unsigned int new_bits) { Q_ASSERT(m_fd >= 0); Kwave::Compression::Type compression; int bits, format = AFMT_QUERY; Kwave::SampleFormat::Format sample_format; // read back current format int err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &format); if (err < 0) return err; format2mode(format, compression, bits, sample_format); // modify the bits per sample bits = new_bits; // activate new format int oldformat = format; format = mode2format(compression, bits, sample_format); err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &format); if (err < 0) return err; if (oldformat != format) return -1; return 0; } //*************************************************************************** int Kwave::RecordOSS::bitsPerSample() { Q_ASSERT(m_fd >= 0); int mask = AFMT_QUERY; int err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &mask); if (err < 0) return err; Kwave::Compression::Type c; int b; Kwave::SampleFormat::Format s; format2mode(mask, c, b, s); return b; } //*************************************************************************** QList Kwave::RecordOSS::detectSampleFormats() { Q_ASSERT(m_fd >= 0); QList formats; formats.clear(); int err = 0; int mask = AFMT_QUERY; err = ioctl(m_fd, SNDCTL_DSP_GETFMTS, &mask); if (err < 0) return formats; // mask out all modes that do not match the current compression // and bits per sample const Kwave::Compression::Type compression = this->compression(); const int bits_per_sample = this->bitsPerSample(); for (unsigned int bit = 0; bit < (sizeof(mask) << 3); bit++) { if (!(mask & (1 << bit))) continue; // format is supported, split into compression, bits, sample format Kwave::Compression::Type c; int b; Kwave::SampleFormat::Format s; format2mode(1 << bit, c, b, s); if (c < 0) continue; // unknown -> skip if ((c == compression) && (b == bits_per_sample)) { // this mode matches -> append it if not already known if (!formats.contains(s)) formats += s; } } return formats; } //*************************************************************************** int Kwave::RecordOSS::setSampleFormat(Kwave::SampleFormat::Format new_format) { Q_ASSERT(m_fd >= 0); Kwave::Compression::Type compression; int bits, format = AFMT_QUERY; Kwave::SampleFormat::Format sample_format; // read back current format int err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &format); if (err < 0) return err; format2mode(format, compression, bits, sample_format); // modify the sample format sample_format = new_format; // activate new format int oldformat = format; format = mode2format(compression, bits, sample_format); err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &format); if (err < 0) return err; if (oldformat != format) return -1; return 0; } //*************************************************************************** Kwave::SampleFormat::Format Kwave::RecordOSS::sampleFormat() { Q_ASSERT(m_fd >= 0); int mask = AFMT_QUERY; int err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &mask); if (err < 0) return Kwave::SampleFormat::Unknown; Kwave::Compression::Type c; int b; Kwave::SampleFormat::Format s; format2mode(mask, c, b, s); return s; } //*************************************************************************** Kwave::byte_order_t Kwave::RecordOSS::endianness() { Q_ASSERT(m_fd >= 0); int mask = AFMT_QUERY; int err = ioctl(m_fd, SNDCTL_DSP_SETFMT, &mask); if (err < 0) return Kwave::UnknownEndian; if (mask & (AFMT_U16_LE | AFMT_S16_LE | AFMT_S24_LE | AFMT_S32_LE)) return Kwave::LittleEndian; if (mask & (AFMT_U16_BE | AFMT_S16_BE | AFMT_S24_BE | AFMT_S32_BE)) return Kwave::BigEndian; if (mask & (AFMT_S8 | AFMT_U8)) return Kwave::CpuEndian; return Kwave::UnknownEndian; } #endif /* HAVE_OSS_SUPPORT */ //*************************************************************************** //*************************************************************************** diff --git a/plugins/sonagram/SonagramPlugin.h b/plugins/sonagram/SonagramPlugin.h index 80a47aeb..413d736a 100644 --- a/plugins/sonagram/SonagramPlugin.h +++ b/plugins/sonagram/SonagramPlugin.h @@ -1,267 +1,266 @@ /*************************************************************************** SonagramPlugin.h - plugin that shows a sonagram window ------------------- begin : Fri Jul 28 2000 copyright : (C) 2000 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef SONAGRAM_PLUGIN_H #define SONAGRAM_PLUGIN_H #include "config.h" -#include #include #include #include #include #include #include #include #include #include #include #include #include "libkwave/FixedPool.h" #include "libkwave/Plugin.h" #include "libkwave/WindowFunction.h" class QImage; class QStringList; /** maximum number of FFT points */ #define MAX_FFT_POINTS 32767 /** maximum number of concurrent FFT jobs */ #define MAX_FFT_JOBS 256 /** maximum number of slices (width of the image) */ #define MAX_SLICES 32767 namespace Kwave { class MultiTrackReader; class OverViewCache; class PluginContext; class SelectionTracker; class SonagramWindow; /** * plugin that shows a sonagram window */ class SonagramPlugin: public Kwave::Plugin { Q_OBJECT public: /** * Constructor * @param parent reference to our plugin manager * @param args argument list [unused] */ SonagramPlugin(QObject *parent, const QVariantList &args); /** Destructor */ virtual ~SonagramPlugin() Q_DECL_OVERRIDE; /** @see Kwave::Plugin::setup() */ virtual QStringList *setup(QStringList &previous_params) Q_DECL_OVERRIDE; /** @see Kwave::Plugin::start() */ virtual int start(QStringList ¶ms) Q_DECL_OVERRIDE; /** * Runns once until all slices of the sonagram are * calculated. * @param params list of strings with parameters * @see Kwave::Plugin::run() */ virtual void run(QStringList params) Q_DECL_OVERRIDE; private: /** internal data of a slice */ typedef struct { /** index of the slice */ unsigned int m_index; /** array with input samples */ double m_input[MAX_FFT_POINTS]; /** FFT output data */ fftw_complex m_output[MAX_FFT_POINTS]; /** rendered FFT result data */ unsigned char m_result[MAX_FFT_POINTS]; } Slice; signals: /** * emitted when a new slice has been calculated in calculateSlice() * @param slice the slice data container, including result */ void sliceAvailable(Kwave::SonagramPlugin::Slice *slice); private slots: /** * validate the sonagram by calling makeAllValid in a background thread */ void validate(); /** * Connected to the SonagramWindow's "destroyed()" signal. * @see class SonagramWindow */ void windowDestroyed(); /** * Internally used to synchronously insert the data of one * sonagram slice int the current image and refresh the * display. * @note DO NOT CALL DIRECTLY! * @param slice a slice data container, including result */ void insertSlice(Kwave::SonagramPlugin::Slice *slice); /** * Updates the overview image under the sonagram */ void refreshOverview(); /** * Connected to the selection tracker's sigTrackInserted. * @param track_id unique ID of the track * @see SelectionTracker::sigTrackInserted */ void slotTrackInserted(const QUuid &track_id); /** * Connected to the selection tracker's sigTrackInserted. * @param track_id unique ID of the track * @see SelectionTracker::sigTrackDeleted */ void slotTrackDeleted(const QUuid &track_id); /** * Connected to the selection tracker's sigInvalidated. * @param track_id UUID of the track or null for "all tracks" * @param first index of the first invalidated sample * @param last index of the last invalidated sample */ void slotInvalidated(const QUuid *track_id, sample_index_t first, sample_index_t last); protected: /** * interpretes a given parameter list and sets up internal * coordinates accordingly * @param params reference to a QStringList with parameters * @return 0 if ok, or an error code if failed */ int interpreteParameters(QStringList ¶ms); private: /** * will be run in a background thread to make all stripes * valid. */ void makeAllValid(); /** * Requests an update of the sonagram or portions of it */ void requestValidation(); /** * do the FFT calculation on a slice * @param slice structure with the input data and output buffer */ void calculateSlice(Kwave::SonagramPlugin::Slice *slice); /** * Creates a new image for the current processing. * If an old image exists, it will be deleted first, a new image * will not be created if either width or height is zero. * The image will get 8 bits depth and use a color or a greyscale * palette. * @param width number of horizontal pixels (slices = signal * length / fft points, rounded up) [1..32767] * @param height number of vertical pixels (= fft points / 2) [1..32767] */ void createNewImage(const unsigned int width, const unsigned int height); private: /** the main view of the plugin, a SonagramWindow */ Kwave::SonagramWindow *m_sonagram_window; /** selection tracker */ Kwave::SelectionTracker *m_selection; /** number of slices (= width of the image in pixels) */ unsigned int m_slices; /** number of fft points */ unsigned int m_fft_points; /** index of the window function */ Kwave::window_function_t m_window_type; /** if true, use color display, else use greyscale */ bool m_color; /** if true, update the sonagram if the signal changes */ bool m_track_changes; /** if true, update the sonagram if the selection changed */ bool m_follow_selection; /** stores the image that is currently in process */ QImage m_image; /** cache with the current signal overview */ Kwave::OverViewCache *m_overview_cache; /** pool of slices */ Kwave::FixedPool m_slice_pool; /** bit field with "is valid" a flag for each stripe */ QBitArray m_valid; /** lock used for tracking running background jobs */ QReadWriteLock m_pending_jobs; /** lock to protect the job list (m_valid) */ QMutex m_lock_job_list; /** the currently running background job */ QFuture m_future; /** timer for refreshing the sonagram */ QTimer m_repaint_timer; }; } #endif /* SONAGRAM_PLUGIN_H */ //*************************************************************************** //***************************************************************************