From 9712fa03b769c3d1ed588b28b256cb53d4a2f15a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 17 Nov 2009 18:33:19 +0100 Subject: [PATCH 001/111] Initial public commit --- .gitignore | 3 + Doxyfile | 1510 ++++++++++++++++++++++++++++ LICENSE.txt | 26 + README.txt | 118 +++ sound/.gitignore | 1 + sound/imp/.gitignore | 1 + sound/imp/audiere_imp.cpp | 94 ++ sound/imp/audiere_imp.h | 73 ++ sound/imp/input_ffmpeg.cpp | 222 ++++ sound/imp/input_ffmpeg.h | 71 ++ sound/imp/openal_ffmpeg.h | 28 + sound/imp/output_openal.cpp | 349 +++++++ sound/imp/output_openal.h | 124 +++ sound/imp/sound_pair.h | 75 ++ sound/input.h | 75 ++ sound/sound.h | 172 ++++ sound/tests/.gitignore | 1 + sound/tests/Makefile | 17 + sound/tests/audiere_test.cpp | 7 + sound/tests/common.cpp | 41 + sound/tests/cow.wav | Bin 0 -> 37546 bytes sound/tests/ffmpeg_openal_test.cpp | 7 + sound/tests/owl.ogg | Bin 0 -> 18641 bytes 23 files changed, 3015 insertions(+) create mode 100644 .gitignore create mode 100644 Doxyfile create mode 100644 LICENSE.txt create mode 100644 README.txt create mode 100644 sound/.gitignore create mode 100644 sound/imp/.gitignore create mode 100644 sound/imp/audiere_imp.cpp create mode 100644 sound/imp/audiere_imp.h create mode 100644 sound/imp/input_ffmpeg.cpp create mode 100644 sound/imp/input_ffmpeg.h create mode 100644 sound/imp/openal_ffmpeg.h create mode 100644 sound/imp/output_openal.cpp create mode 100644 sound/imp/output_openal.h create mode 100644 sound/imp/sound_pair.h create mode 100644 sound/input.h create mode 100644 sound/sound.h create mode 100644 sound/tests/.gitignore create mode 100644 sound/tests/Makefile create mode 100644 sound/tests/audiere_test.cpp create mode 100644 sound/tests/common.cpp create mode 100644 sound/tests/cow.wav create mode 100644 sound/tests/ffmpeg_openal_test.cpp create mode 100644 sound/tests/owl.ogg diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..cd24d7897 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +upload_docs.sh +docs +*~ diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 000000000..4dfff9188 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1510 @@ +# Doxyfile 1.5.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = GOOI + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, +# Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = sound + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */tests/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = docs + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to FRAME, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + +GENERATE_TREEVIEW = NONE + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 000000000..1b98eeb9b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,26 @@ +Game-oriented object interfaces (GOOI) is licensed under the +'zlib/libpng' license: + +---- + +Copyright (c) 2009 Nicolay Korslund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + diff --git a/README.txt b/README.txt new file mode 100644 index 000000000..0b0befca5 --- /dev/null +++ b/README.txt @@ -0,0 +1,118 @@ +Welcome to GOOI v0.1 +-------------------- + +Written by: Nicolay Korslund (korslund@gmail.com) +License: zlib/png (see LICENSE.txt) +WWW: http://asm-soft.com/gooi/ +Documentation: http://asm-soft.com/gooi/docs + + + +GOOI stands for Game-Oriented Object Interfaces. It is meant to become +a small set of generic interfaces for various game middleware +libraries, such as sound, input, graphics, and so on. It consists of +several independent modules, one for each of these areas. These may be +used together to build an entire game engine, or they can be used +individually as separate libraries. + +However, GOOI does NOT actually implement a game engine, or any new +fundamental functionality. More on that below. + +Currently there is only the Sound module, but more will come in the +future (including input, 2D/3D graphics, GUI, physics, file +system/archive access, and more.) + + +Main idea +--------- + +The idea behind to provide a uniform, consistent interface to other +game libraries. The library does not provide ANY functionality on its +own. Instead it connects to a backend implementation of your choice. + +The Sound module, for example, currently has backends for OpenAL +(output only), FFmpeg (input only) and for Audiere. Hopefully we'll +soon add IrrKlang, FMod, DirectSound and Miles to that. It can combine +libraries to get more complete functionality (like using OpenAL for +output and FFmpeg to decode sound files), and it's also easy to write +your own backend if you're using a different (or home-brewed) sound +system. + +Regardless of what backend you use, the front-end interface (found in +sound/sound.h) is identical, and as a library user you shouldn't +notice much difference at all if you swap one backend for another at a +later point. + +The goal in the long run is to support a wide variety of game-related +libraries, and as many backend libraries (free and commercial) as +possible, so that you the user will have to write as little code as +possible. + + + +What is it good for +------------------- + +The main point of GOOI, as we said above, is that it connects to any +library of your choice "behind the scenes" but provides the same, +super-simple interface front-end for all of them. There can benefit +you in many ways: + +- If you want to use a new library that GOOI support. You don't + have to scour the net for tutorials and usage examples, since much + of the common usage code is already included in the implementation + classes. + +- If you don't want to pollute your code with library-specific code. + The GOOI interfaces can help you keep your code clean, and its user + interface is often simpler than the exteral library one. + +- If you are creating a library that depends on a specific feature + (such as sound), but you don't want to lock your users into any + specific sound library. GOOI works as an abstraction that lets your + users select their own implementation. My own Monster scripting + language ( http://monsterscript.net ) will use this tactic, to + provide native-but-generic sound, input and GUI support, among other + features. + +- If you want to support multiple backends, or make it possible to + easily switch backends later. You can select backends at compile + time or even at runtime. Maybe you decide to switch to to a + commercial library at a late stage in development, or you discover + that your favorite backend doesn't work on all the platforms you + want to reach. + +The GOOI implementations are extremely light-weight - often just one +or two cpp/h pairs. You plug them directly into your program, there's +no separate build step required. + +Since the library aims to be very modularly put together, you can +also, in many cases, just copy-and-paste the parts you need and ignore +the rest. Or modify stuff without fearing that the whole 'system' will +come crashing down, because there is no big 'system' to speak of. + + +Past and future +--------------- + +GOOI started out as a spin-off from OpenMW, another project of mine +( http://openmw.sourceforge.net ). OpenMW is an attempt to recreate +the engine behind the commercial game Morrowind, using only open +source software. + +The projects are still tightly interlinked, and the will continue to +be until OpenMW is finished. That means that all near-future work on +GOOI for my part will be more or less guided by what OpenMW needs. But +I'll gladly accept external contributions that are not OpenMW-related. + + +Conclusion +---------- + +As you might have guessed, GOOI is more a concept in development than +a finished library right now. + +All feedback, ideas, concepts, questions and code are very +welcome. Send them to: korslund@gmail.com + +I will put up a forum later as well if there's enough interest. diff --git a/sound/.gitignore b/sound/.gitignore new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/sound/.gitignore @@ -0,0 +1 @@ + diff --git a/sound/imp/.gitignore b/sound/imp/.gitignore new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/sound/imp/.gitignore @@ -0,0 +1 @@ + diff --git a/sound/imp/audiere_imp.cpp b/sound/imp/audiere_imp.cpp new file mode 100644 index 000000000..ea094409f --- /dev/null +++ b/sound/imp/audiere_imp.cpp @@ -0,0 +1,94 @@ +#include "audiere_imp.h" + +// Exception handling +class Audiere_Exception : public std::exception +{ + std::string msg; + + public: + + Audiere_Exception(const std::string &m) : msg(m) {} + ~Audiere_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw Audiere_Exception("Audiere exception: " + msg); +} + +using namespace audiere; +using namespace GOOI::Sound; + +AudiereManager::AudiereManager() +{ + needsUpdate = false; + has3D = false; + canRepeatStream = true; + canLoadFile = true; + canLoadSource = false; + + device = OpenDevice(""); + + if(device == NULL) + fail("Failed to open device"); +} + +// --- Manager --- + +Sound *AudiereManager::load(const std::string &file, bool stream) +{ return new AudiereSound(file, device, stream); } + + +// --- Sound --- + +AudiereSound::AudiereSound(const std::string &file, + AudioDevicePtr _device, + bool _stream) + : device(_device), stream(_stream) +{ + sample = OpenSampleSource(file.c_str()); + if(!sample) + fail("Couldn't load file " + file); + + buf = CreateSampleBuffer(sample); +} + +Instance *AudiereSound::getInstance(bool is3d, bool repeat) +{ + // Ignore is3d. Audiere doesn't implement 3d sound. We could make a + // hack software 3D implementation later, but it's not that + // important. + + SampleSourcePtr sample = buf->openStream(); + if(!sample) + fail("Failed to open sample stream"); + + OutputStreamPtr sound = OpenSound(device, sample, stream); + + if(repeat) + sound->setRepeat(true); + + return new AudiereInstance(sound); +} + + +// --- Instance --- + +AudiereInstance::AudiereInstance(OutputStreamPtr _sound) + : sound(_sound) {} + +void AudiereInstance::play() +{ sound->play(); } + +void AudiereInstance::stop() +{ sound->stop(); } + +void AudiereInstance::pause() +{ stop(); } + +bool AudiereInstance::isPlaying() +{ return sound->isPlaying(); } + +void AudiereInstance::setVolume(float vol) +{ sound->setVolume(vol); } diff --git a/sound/imp/audiere_imp.h b/sound/imp/audiere_imp.h new file mode 100644 index 000000000..9f667a98a --- /dev/null +++ b/sound/imp/audiere_imp.h @@ -0,0 +1,73 @@ +#ifndef GOOI_SOUND_AUDIERE_H +#define GOOI_SOUND_AUDIERE_H + +#include "../sound.h" + +#include +#include + +namespace GOOI { +namespace Sound { + +/// Implementation of Sound::Manager for Audiere +class AudiereManager : public Manager +{ + audiere::AudioDevicePtr device; + + public: + AudiereManager(); + + virtual Sound *load(const std::string &file, bool stream=false); + + /// disabled + virtual Sound *load(InputSource *input, bool stream=false) + { assert(0); } + /// disabled + virtual void update() { assert(0); } + /// disabled + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { assert(0); }; +}; + +/// Audiere Sound implementation +class AudiereSound : public Sound +{ + audiere::AudioDevicePtr device; + audiere::SampleSourcePtr sample; + audiere::SampleBufferPtr buf; + + bool stream; + + public: + virtual Instance *getInstance(bool is3d, bool repeat); + virtual void drop() + { delete this; } + + AudiereSound(const std::string &file, audiere::AudioDevicePtr device, + bool stream); +}; + +/// Audiere Instance implementation +class AudiereInstance : public Instance +{ + audiere::OutputStreamPtr sound; + + public: + virtual void play(); + virtual void stop(); + virtual void pause(); + virtual bool isPlaying(); + virtual void setVolume(float); + /// disabled + virtual void setPos(float x, float y, float z) + { assert(0); } + virtual void drop() + { delete this; } + + AudiereInstance(audiere::OutputStreamPtr); +}; + +}} // Namespace +#endif diff --git a/sound/imp/input_ffmpeg.cpp b/sound/imp/input_ffmpeg.cpp new file mode 100644 index 000000000..c73689b78 --- /dev/null +++ b/sound/imp/input_ffmpeg.cpp @@ -0,0 +1,222 @@ +#include "input_ffmpeg.h" +#include + +using namespace GOOI::Sound; + +// Static output buffer. Not thread safe, but supports multiple +// streams operated from the same thread. +static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; +bool FFM_InputManager::init = false; + +FFM_Exception::FFM_Exception(const std::string &m) + : msg(m) {} + +const char* FFM_Exception::what() const throw() +{ return msg.c_str(); } + +FFM_Exception::~FFM_Exception() throw() {} + +static void fail(const std::string &msg) +{ + throw FFM_Exception("FFMpeg exception: " + msg); +} + + +// --- Manager --- + +FFM_InputManager::FFM_InputManager() +{ + if(!init) + { + av_register_all(); + av_log_set_level(AV_LOG_ERROR); + init = true; + } +} + +InputSource *FFM_InputManager::load(const std::string &file) +{ return new FFM_InputSource(file); } + + +// --- Source --- + +FFM_InputSource::FFM_InputSource(const std::string &file) +{ + // FFmpeg doesn't handle several instances from one source. So we + // just store the filename. + name = file; +} + +InputStream *FFM_InputSource::getStream() +{ return new FFM_InputStream(name); } + +void FFM_InputSource::drop() +{ delete this; } + + +// --- Stream --- + +FFM_InputStream::FFM_InputStream(const std::string &file) +{ + std::string msg; + AVCodec *codec; + + empty = false; + + if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) + fail("Error loading audio file " + file); + + if(av_find_stream_info(FmtCtx) < 0) + { + msg = "Error in file stream " + file; + goto err; + } + + // Pick the first audio stream, if any + for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++) + { + // Pick the first audio stream + if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO) + break; + } + + if(StreamNum == FmtCtx->nb_streams) + fail("File " + file + " didn't contain any audio streams"); + + // Open the decoder + CodecCtx = FmtCtx->streams[StreamNum]->codec; + codec = avcodec_find_decoder(CodecCtx->codec_id); + + if(!codec || avcodec_open(CodecCtx, codec) < 0) + { + msg = "Error loading " + file + ": "; + if(codec) + msg += "coded error"; + else + msg += "no codec found"; + goto err; + } + + // No errors, we're done + return; + + // Handle errors + err: + av_close_input_file(FmtCtx); + fail(msg); +} + +FFM_InputStream::~FFM_InputStream() +{ + avcodec_close(CodecCtx); + av_close_input_file(FmtCtx); +} + +void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + if(rate) *rate = CodecCtx->sample_rate; + if(channels) *channels = CodecCtx->channels; + if(bits) *bits = 16; +} + +uint32_t FFM_InputStream::getData(void *data, uint32_t length) +{ + if(empty) return 0; + + uint32_t left = length; + uint8_t *outPtr = (uint8_t*)data; + + // First, copy over any stored data we might be sitting on + { + int s = storage.size(); + int copy = s; + if(s) + { + // Make sure there's room + if(copy > left) + copy = left; + + // Copy + memcpy(outPtr, &storage[0], copy); + outPtr += copy; + left -= copy; + + // Is there anything left in the storage? + s -= copy; + if(s) + { + assert(left == 0); + + // Move it to the start and resize + memmove(&storage[0], &storage[copy], s); + storage.resize(s); + } + } + } + + // Next, get more input data from stream, and decode it + while(left) + { + AVPacket packet; + + // Get the next packet, if any + if(av_read_frame(FmtCtx, &packet) < 0) + break; + + // We only allow one stream per file at the moment + assert(StreamNum == packet.stream_index); + + // Decode the packet + int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; + int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf, + &len, packet.data, packet.size); + assert(tmp < 0 || tmp == packet.size); + + // We don't need the input packet any longer + av_free_packet(&packet); + + if(tmp < 0) + fail("Error decoding audio stream"); + + // Copy whatever data we got, and advance the pointer + if(len > 0) + { + // copy = how many bytes do we copy now + int copy = len; + if(copy > left) + copy = left; + + // len = how many bytes are left uncopied + len -= copy; + + // copy data + memcpy(outPtr, outBuf, copy); + + // left = how much space is left in the caller output + // buffer + left -= copy; + outPtr += copy; + assert(left >= 0); + + if(len > 0) + { + // There were uncopied bytes. Store them for later. + assert(left == 0); + storage.resize(len); + memcpy(&storage[0], outBuf, len); + } + } + } + + // End of loop. Return the number of bytes copied. + assert(left <= length); + + // If we're returning less than asked for, then we're done + if(left > 0) + empty = true; + + return length - left; +} + +void FFM_InputStream::drop() +{ delete this; } diff --git a/sound/imp/input_ffmpeg.h b/sound/imp/input_ffmpeg.h new file mode 100644 index 000000000..17c8f7352 --- /dev/null +++ b/sound/imp/input_ffmpeg.h @@ -0,0 +1,71 @@ +#ifndef GOOI_SOUND_FFMPEG_H +#define GOOI_SOUND_FFMPEG_H + +#include "../input.h" +#include +#include + +extern "C" +{ +#include +#include +} + +namespace GOOI { +namespace Sound { + +/// FFmpeg exception +class FFM_Exception : public std::exception +{ + std::string msg; + + public: + + FFM_Exception(const std::string &m); + ~FFM_Exception() throw(); + virtual const char* what() const throw(); +}; + +/// FFMpeg implementation of InputManager +class FFM_InputManager : public InputManager +{ + static bool init; + + public: + FFM_InputManager(); + virtual InputSource *load(const std::string &file); +}; + +/// FFMpeg implementation of InputSource +class FFM_InputSource : public InputSource +{ + std::string name; + + public: + FFM_InputSource(const std::string &file); + + virtual InputStream *getStream(); + virtual void drop(); +}; + +/// FFMpeg implementation of InputStream +class FFM_InputStream : public InputStream +{ + AVFormatContext *FmtCtx; + AVCodecContext *CodecCtx; + int StreamNum; + bool empty; + + std::vector storage; + + public: + FFM_InputStream(const std::string &file); + ~FFM_InputStream(); + + virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + virtual uint32_t getData(void *data, uint32_t length); + virtual void drop(); +}; + +}} // namespaces +#endif diff --git a/sound/imp/openal_ffmpeg.h b/sound/imp/openal_ffmpeg.h new file mode 100644 index 000000000..92fd32553 --- /dev/null +++ b/sound/imp/openal_ffmpeg.h @@ -0,0 +1,28 @@ +#ifndef GOOI_FFMPEG_OPENAL_H +#define GOOI_FFMPEG_OPENAL_H + +#include "sound_pair.h" +#include "input_ffmpeg.h" +#include "output_openal.h" + +namespace GOOI { +namespace Sound { + +/// A PairManager filter that adds FFmpeg decoding to OpenAL +class OpenAL_FFM_Manager : public PairManager +{ + public: + OpenAL_FFM_Manager() + { + set(new FFM_InputManager, + new OpenAL_Manager); + } + ~OpenAL_FFM_Manager() + { + delete snd; + delete inp; + } +}; + +}} +#endif diff --git a/sound/imp/output_openal.cpp b/sound/imp/output_openal.cpp new file mode 100644 index 000000000..f7dc11507 --- /dev/null +++ b/sound/imp/output_openal.cpp @@ -0,0 +1,349 @@ +#include "output_openal.h" +#include + +#include + +using namespace GOOI::Sound; + + +// ---- Helper functions and classes ---- + +class OpenAL_Exception : public std::exception +{ + std::string msg; + + public: + + OpenAL_Exception(const std::string &m) : msg(m) {} + ~OpenAL_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw OpenAL_Exception("OpenAL exception: " + msg); +} + +static void checkALError(const std::string &msg) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + fail("\"" + std::string(alGetString(err)) + "\" while " + msg); +} + +static void getALFormat(InputStream *inp, int &fmt, int &rate) +{ + int ch, bits; + inp->getInfo(&rate, &ch, &bits); + + fmt = 0; + + if(bits == 8) + { + if(ch == 1) fmt = AL_FORMAT_MONO8; + if(ch == 2) fmt = AL_FORMAT_STEREO8; + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8"); + } + } + if(bits == 16) + { + if(ch == 1) fmt = AL_FORMAT_MONO16; + if(ch == 2) fmt = AL_FORMAT_STEREO16; + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16"); + } + } + + if(fmt == 0) + fail("Unsupported input format"); +} + + +// ---- Manager ---- + +OpenAL_Manager::OpenAL_Manager() + : Context(NULL), Device(NULL) +{ + needsUpdate = true; + has3D = true; + canRepeatStream = false; + canLoadFile = false; + canLoadSource = true; + + // Set up sound system + Device = alcOpenDevice(NULL); + Context = alcCreateContext(Device, NULL); + + if(!Device || !Context) + fail("Failed to initialize context or device"); + + alcMakeContextCurrent(Context); +} + +OpenAL_Manager::~OpenAL_Manager() +{ + // Deinitialize sound system + alcMakeContextCurrent(NULL); + if(Context) alcDestroyContext(Context); + if(Device) alcCloseDevice(Device); +} + +Sound *OpenAL_Manager::load(const std::string &file, bool stream) +{ assert(0 && "OpenAL cannot decode files"); } + +Sound *OpenAL_Manager::load(InputSource *source, bool stream) +{ return new OpenAL_Sound(source, this, stream); } + +void OpenAL_Manager::update() +{ + // Loop through all the streaming sounds and update them + LST::iterator it, next; + for(it = streaming.begin(); + it != streaming.end(); + it++) + { + (*it)->update(); + } +} + +void OpenAL_Manager::setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) +{ + ALfloat orient[6]; + orient[0] = fx; + orient[1] = fy; + orient[2] = fz; + orient[3] = ux; + orient[4] = uy; + orient[5] = uz; + alListener3f(AL_POSITION, x, y, z); + alListenerfv(AL_ORIENTATION, orient); + checkALError("setting listener position"); +} + +OpenAL_Manager::LST::iterator OpenAL_Manager::add_stream(OpenAL_Stream_Instance* inst) +{ + streaming.push_front(inst); + return streaming.begin(); +} + +void OpenAL_Manager::remove_stream(LST::iterator it) +{ + streaming.erase(it); +} + + +// ---- Sound ---- + +OpenAL_Sound::~OpenAL_Sound() +{ + // Kill the input source + if(source) source->drop(); + + // And any allocated buffers + if(bufferID) + alDeleteBuffers(1, &bufferID); +} + +Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) +{ + assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); + + if(stream) + return new OpenAL_Stream_Instance(source->getStream(), owner); + + // Load the buffer if it hasn't been done already + if(bufferID == 0) + { + // Get an input stream and load the file from it + InputStream *inp = source->getStream(); + + std::vector buffer; + + // Add 32 kb at each increment + const int ADD = 32*1024; + + // Fill the buffer. We increase the buffer until it's large + // enough to fit all the data. + while(true) + { + // Increase the buffer + int oldlen = buffer.size(); + buffer.resize(oldlen+ADD); + + // Read the data + size_t len = inp->getData(&buffer[oldlen], ADD); + + // If we read less than requested, we're done. + if(len < ADD) + { + // Downsize the buffer to the right size + buffer.resize(oldlen+len); + break; + } + } + + // Get the format + int fmt, rate; + getALFormat(inp, fmt, rate); + + // We don't need the file anymore + inp->drop(); + source->drop(); + source = NULL; + + // Move the data into OpenAL + alGenBuffers(1, &bufferID); + alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); + checkALError("loading sound buffer"); + } // End of buffer loading + + // At this point, the file data has been loaded into the buffer + // in 'bufferID', and we should be ready to go. + assert(bufferID != 0); + + return new OpenAL_Simple_Instance(bufferID); +} + + +// ---- OpenAL_Instance_Base ---- + +void OpenAL_Instance_Base::play() +{ + alSourcePlay(inst); + checkALError("starting playback"); +} + +void OpenAL_Instance_Base::stop() +{ + alSourceStop(inst); + checkALError("stopping"); +} + +void OpenAL_Instance_Base::pause() +{ + alSourcePause(inst); + checkALError("pausing"); +} + +bool OpenAL_Instance_Base::isPlaying() +{ + ALint state; + alGetSourcei(inst, AL_SOURCE_STATE, &state); + + return state == AL_PLAYING; +} + +void OpenAL_Instance_Base::setVolume(float volume) +{ + if(volume > 1.0) volume = 1.0; + if(volume < 0.0) volume = 0.0; + alSourcef(inst, AL_GAIN, volume); + checkALError("setting volume"); +} + +void OpenAL_Instance_Base::setPos(float x, float y, float z) +{ + alSource3f(inst, AL_POSITION, x, y, z); + checkALError("setting position"); +} + + +// ---- OpenAL_Simple_Instance ---- + +OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) +{ + // Create instance and associate buffer + alGenSources(1, &inst); + alSourcei(inst, AL_BUFFER, buf); +} + +OpenAL_Simple_Instance::~OpenAL_Simple_Instance() +{ + // Stop + alSourceStop(inst); + + // Return sound + alDeleteSources(1, &inst); +} + + +// ---- OpenAL_Stream_Instance ---- + +OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, + OpenAL_Manager *_owner) + : stream(_stream), owner(_owner) +{ + // Deduce the file format from the stream info + getALFormat(stream, fmt, rate); + + // Create the buffers and the sound instance + alGenBuffers(BUFS, bufs); + alGenSources(1, &inst); + + checkALError("initializing"); + + // Fill the buffers and que them + for(int i=0; iadd_stream(this); +} + +void OpenAL_Stream_Instance::queueBuffer(ALuint bId) +{ + char buf[SIZE]; + + // Get the data + int len = stream->getData(buf, SIZE); + if(len == 0) + return; + + // .. and stash it + alBufferData(bId, fmt, buf, len, rate); + alSourceQueueBuffers(inst, 1, &bId); +} + +OpenAL_Stream_Instance::~OpenAL_Stream_Instance() +{ + // Remove ourselves from streaming list + owner->remove_stream(lit); + + // Stop + alSourceStop(inst); + + // Kill the input stream + stream->drop(); + + // Return sound + alDeleteSources(1, &inst); + + // Delete buffers + alDeleteBuffers(BUFS, bufs); +} + +void OpenAL_Stream_Instance::update() +{ + ALint count; + alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); + + for(int i = 0;i < count;i++) + { + // Unque a finished buffer + ALuint bId; + alSourceUnqueueBuffers(inst, 1, &bId); + + // Queue a new buffer + queueBuffer(bId); + } +} diff --git a/sound/imp/output_openal.h b/sound/imp/output_openal.h new file mode 100644 index 000000000..e2e506aa2 --- /dev/null +++ b/sound/imp/output_openal.h @@ -0,0 +1,124 @@ +#ifndef GOOI_SOUND_OPENAL_H +#define GOOI_SOUND_OPENAL_H + +#include "../sound.h" + +#include +#include +#include + +namespace GOOI { +namespace Sound { + +class OpenAL_Stream_Instance; + +/// OpenAL implementation of Manager +class OpenAL_Manager : public Manager +{ +public: + // List of all streaming sounds - these need to be updated regularly + typedef std::list LST; + + OpenAL_Manager(); + virtual ~OpenAL_Manager(); + + LST::iterator add_stream(OpenAL_Stream_Instance*); + void remove_stream(LST::iterator); + + virtual Sound *load(const std::string &file, bool stream=false); + virtual Sound *load(InputSource* input, bool stream=false); + virtual void update(); + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz); + + private: + ALCdevice *Device; + ALCcontext *Context; + + LST streaming; +}; + +/// OpenAL implementation of Sound +class OpenAL_Sound : public Sound +{ + InputSource *source; + OpenAL_Manager *owner; + bool stream; + + // Used for non-streaming files, contains the entire sound buffer if + // non-zero + ALuint bufferID; + + public: + OpenAL_Sound(InputSource *src, OpenAL_Manager *own, bool str) + : source(src), owner(own), stream(str), bufferID(0) {} + ~OpenAL_Sound(); + + virtual Instance *getInstance(bool is3d, bool repeat); + void drop() { delete this; } +}; + +/// Shared parent class that holds an OpenAL sound instance. Just used +/// for shared functionality, has no setup or cleanup code. +class OpenAL_Instance_Base : public Instance +{ + protected: + ALuint inst; + + public: + void drop() { delete this; } + virtual void play(); + virtual void stop(); + virtual void pause(); + virtual bool isPlaying(); + virtual void setVolume(float); + virtual void setPos(float x, float y, float z); +}; + +/// Non-streaming OpenAL-implementation of Instance. Uses a shared +/// sound buffer in OpenAL_Sound. +class OpenAL_Simple_Instance : public OpenAL_Instance_Base +{ + public: + OpenAL_Simple_Instance(ALuint buf); + ~OpenAL_Simple_Instance(); +}; + +/// Streaming OpenAL-implementation of Instance. +class OpenAL_Stream_Instance : public OpenAL_Instance_Base +{ + // Since OpenAL streams have to be updated manually each frame, we + // need to have a sufficiently large buffer so that we don't run out + // of data in the mean time. Each instance will take around 512 Kb + // of memory, independent of how large the file is. + static const int BUFS = 4; + static const int SIZE = 128*1024; + + // Buffers + ALuint bufs[BUFS]; + + // Sound format settings + int rate, fmt; + + // Source of data + InputStream *stream; + + OpenAL_Manager *owner; + + // List iterator, used for removing ourselves from the streaming + // list when we're deleted. + OpenAL_Manager::LST::iterator lit; + + // Load and queue a new buffer + void queueBuffer(ALuint buffer); + +public: + OpenAL_Stream_Instance(InputStream*, OpenAL_Manager*); + ~OpenAL_Stream_Instance(); + + void update(); +}; + +}} // namespaces +#endif diff --git a/sound/imp/sound_pair.h b/sound/imp/sound_pair.h new file mode 100644 index 000000000..751c72b1a --- /dev/null +++ b/sound/imp/sound_pair.h @@ -0,0 +1,75 @@ +#ifndef GOOI_SOUND_PAIR_H +#define GOOI_SOUND_PAIR_H + +#include "sound.h" + +#include + +namespace GOOI { +namespace Sound { + +/** + @brief This filter class adds file loading capabilities to a + Sound::Manager class, by associating an InputManager with it. + + The class takes an existing Manager able to load streams, and + associates an InputManager with it. The combined class is able to + load files directly. + + Example: + \code + + // Combine FFmpeg input and OpenAL output. OpenAL cannot decode + // sound files on its own. + SoundPairManager mg(new FFM_InputManager, new OpenAL_Manager); + + // We can now load filenames directly. + mg.load("file1.mp3"); + \endcode +*/ +class PairManager : public Manager +{ + protected: + Manager *snd; + InputManager *inp; + + public: + /// Empty constructor + PairManager() {} + + /// Assign an input manager and a sound manager to this object + PairManager(InputManager *_inp, Manager *_snd) + { set(_inp, _snd); } + + /// Assign an input manager and a sound manager to this object + void set(InputManager *_inp, Manager *_snd) + { + inp = _inp; + snd = _snd; + + needsUpdate = snd->needsUpdate; + has3D = snd->has3D; + canRepeatStream = snd->canRepeatStream; + + // Both these should be true, or the use of this class is pretty + // pointless + canLoadSource = snd->canLoadSource; + canLoadFile = canLoadSource; + assert(canLoadSource && canLoadFile); + } + + virtual Sound *load(const std::string &file, bool stream=false) + { return load(inp->load(file), stream); } + + virtual Sound *load(InputSource *input, bool stream=false) + { return snd->load(input, stream); } + + virtual void update() { snd->update(); } + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); } +}; + +}} +#endif diff --git a/sound/input.h b/sound/input.h new file mode 100644 index 000000000..a0d2826aa --- /dev/null +++ b/sound/input.h @@ -0,0 +1,75 @@ +#ifndef GOOI_SOUND_INPUT_H +#define GOOI_SOUND_INPUT_H + +#include +#include + +namespace GOOI { +namespace Sound { + +/// An abstract interface for a read-once stream of audio data. +/** All instances of this is created through InputSource. Objects + should be manually deleted through a call to drop() when they are + no longer needed. +*/ +class InputStream +{ + public: + /// Get the sample rate, number of channels, and bits per + /// sample. NULL parameters are ignored. + virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0; + + /// Get decoded sound data from the stream. + /** Stores 'length' bytes (or less) in the buffer pointed to by + 'output'. Returns the number of bytes written. The function will + only return less than 'length' at the end of the stream. When + the stream is empty, all subsequent calls will return zero. + + @param output where to store data + @param length number of bytes to get + @return number of bytes actually written + */ + virtual uint32_t getData(void *output, uint32_t length) = 0; + + /// Kill this object + virtual void drop() = 0; + + /// Virtual destructor + virtual ~InputStream() {} +}; + +/// Abstract interface representing one sound source. +/** A sound source may represent one sound file or buffer, and is a + factory for producing InputStream objects from that + sound. Instances of this class are created by an InputManager. All + instances should be deleted through drop() when they are no longer + needed. + */ +class InputSource +{ + public: + /// Create a stream from this sound + virtual InputStream *getStream() = 0; + + /// Kill this object + virtual void drop() = 0; + + /// Virtual destructor + virtual ~InputSource() {} +}; + +/// Main interface to a sound decoder backend. +/** An input manager is a factory of InputSource objects. + */ +class InputManager +{ + public: + /// Load a sound input source from file + virtual InputSource *load(const std::string &file) = 0; + + /// Virtual destructor + virtual ~InputManager() {} +}; + +}} // namespaces +#endif diff --git a/sound/sound.h b/sound/sound.h new file mode 100644 index 000000000..509efa816 --- /dev/null +++ b/sound/sound.h @@ -0,0 +1,172 @@ +#ifndef GOOI_SOUND_SOUND_H +#define GOOI_SOUND_SOUND_H + +#include +#include "input.h" + +namespace GOOI { +namespace Sound { + +/// Abstract interface for sound instances +/** This class represents one sound instance, which may be played, + stopped, paused and so on. Instances are created from the Sound + class. All instances must be terminated manually using the drop() + function when they are no longer in use. +*/ +class Instance +{ + public: + /// Play or resume the sound + virtual void play() = 0; + + /// Stop the sound + virtual void stop() = 0; + + /// Pause the sound, may be resumed later + virtual void pause() = 0; + + /// Check if the sound is still playing + virtual bool isPlaying() = 0; + + /// Set the volume. The parameter must be between 0.0 and 1.0. + virtual void setVolume(float) = 0; + + /// Set the position. May not have any effect on 2D sounds. + virtual void setPos(float x, float y, float z) = 0; + + /// Kill the current object + virtual void drop() = 0; + + /// Virtual destructor + virtual ~Instance() {} +}; + +/// Abstract interface for sound files or sources +/** This class acts as a factory for sound Instance objects. + Implementations may choose to store shared sound buffers or other + optimizations in subclasses of Sound. Objects of this class are + created through the Manager class. All objects of this class + should be terminated manually using the drop() function when they + are no longer in use. +*/ +class Sound +{ + public: + /** + @brief Create an instance of this sound + + See also the capability flags in the Manager class. + + @param is3d true if this the sound is to be 3d enabled + @param repeat true if the sound should loop + @return new Instance object + */ + virtual Instance *getInstance(bool is3d, bool repeat) = 0; + + // Some prefab functions + + /// Shortcut for creating 3D instances + Instance *get3D(bool loop=false) + { return getInstance(true, loop); } + /// Shortcut for creating 2D instances + Instance *get2D(bool loop=false) + { return getInstance(false, loop); } + + /// Kill the current object + virtual void drop() = 0; + + /// Virtual destructor + virtual ~Sound() {} +}; + +/// Abstract interface for the main sound manager class +/** The sound manager is used to load sound files and is a factory for + Sound objects. It is the main entry point to a given sound system + implementation. + + The class also contains a set of public bools which describe the + capabilities the particular system. These should be set by + implementations (base classes) in their respective constructors. + */ +class Manager +{ + public: + /** @brief If set to true, you should call update() regularly (every frame + or so) on this sound manager. If false, update() should not be + called. + */ + bool needsUpdate; + + /** @brief true if 3D functions are available. If false, all use of + 3D sounds and calls to setPos / setListenerPos will result in + undefined behavior. + */ + bool has3D; + + /** @brief true if 'repeat' and 'stream' can be used simultaneously. + If false, repeating a streamed sound will give undefined + behavior. + */ + bool canRepeatStream; + + /// true if we can load sounds directly from file + bool canLoadFile; + + /// true if we can load sounds from an InputSource + bool canLoadSource; + + /** + @brief Load a sound from an input source. Only valid if + canLoadSource is true. + + This function loads a sound from a given stream as defined by + InputSource and InputStream. The InputSource and all streams + created from it will be dropped when drop() is called on the + owning sound / instance. + + @param input the input source + @param stream true if the file should be streamed. + Implementations may use this for optimizing playback of + large files, but they are not required to. + @return a new Sound object + */ + virtual Sound *load(InputSource *input, bool stream=false) = 0; + + /** + @brief Load a sound directly from file. Only valid if canLoadFile + is true. + + @param file filename + @param stream true if the file should be streamed + @see load(InputSource*,bool) + */ + virtual Sound *load(const std::string &file, bool stream=false) = 0; + + /// Call this every frame if needsUpdate is true + /** + Update function that should be called regularly (about every + frame in a normal game setting.) Implementions may use this to + fill streaming buffers and similar. Implementations that do not + need this should set needsUpdate to false. + */ + virtual void update() = 0; + + /// Set listener position (coordinates, front and up vectors) + /** + Only valid if has3D is true. + + @param x,y,z listener position + @param fx,fy,fz listener's looking direction + @param ux,uy,uz listener's up direction + */ + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) = 0; + + /// Virtual destructor + virtual ~Manager() {} +}; + +}} // Namespaces + +#endif diff --git a/sound/tests/.gitignore b/sound/tests/.gitignore new file mode 100644 index 000000000..814490404 --- /dev/null +++ b/sound/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/sound/tests/Makefile b/sound/tests/Makefile new file mode 100644 index 000000000..4cdf5ab58 --- /dev/null +++ b/sound/tests/Makefile @@ -0,0 +1,17 @@ +GCC=g++ -I../ -I../imp/ + +all: audiere_test ffmpeg_openal_test + +L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +L_OPENAL=$(shell pkg-config --libs openal) + +L_AUDIERE=-laudiere + +ffmpeg_openal_test: ffmpeg_openal_test.cpp ../imp/input_ffmpeg.cpp ../imp/output_openal.cpp + $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) + +audiere_test: audiere_test.cpp ../imp/audiere_imp.cpp + $(GCC) $^ -o $@ $(L_AUDIERE) + +clean: + rm *_test diff --git a/sound/tests/audiere_test.cpp b/sound/tests/audiere_test.cpp new file mode 100644 index 000000000..16b132272 --- /dev/null +++ b/sound/tests/audiere_test.cpp @@ -0,0 +1,7 @@ +#include "audiere_imp.h" + +using namespace GOOI::Sound; + +AudiereManager mg; + +#include "common.cpp" diff --git a/sound/tests/common.cpp b/sound/tests/common.cpp new file mode 100644 index 000000000..78dbce5b2 --- /dev/null +++ b/sound/tests/common.cpp @@ -0,0 +1,41 @@ +// This file is included directly into the test programs + +#include +#include + +using namespace std; + +void play(const char* name, bool music=false) +{ + cout << "Playing " << name << "\n"; + + Sound *snd = NULL; + Instance *s = NULL; + + try + { + snd = mg.load(name, music); + s = snd->getInstance(false, false); + s->play(); + + while(s->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } + + if(s) s->drop(); + if(snd) snd->drop(); +} + +int main() +{ + play("cow.wav"); + play("owl.ogg", true); + return 0; +} diff --git a/sound/tests/cow.wav b/sound/tests/cow.wav new file mode 100644 index 0000000000000000000000000000000000000000..494e6c4ac12a3d318532fdf8178d72f775bc40af GIT binary patch literal 37546 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�b}Ou2(@Nod9rZet}z#nWoL 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"`&tnb`wrwjv1+m1l^X62V>RQ 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 uxXE2GB)VyFe*N* Date: Sat, 21 Nov 2009 11:41:44 +0100 Subject: [PATCH 002/111] Renamed to MANGLE --- Doxyfile | 2 +- LICENSE.txt | 2 +- README.txt | 44 +++++++++++++++--------------- sound/imp/audiere_imp.cpp | 2 +- sound/imp/audiere_imp.h | 6 ++-- sound/imp/input_ffmpeg.cpp | 2 +- sound/imp/input_ffmpeg.h | 6 ++-- sound/imp/openal_ffmpeg.h | 6 ++-- sound/imp/output_openal.cpp | 2 +- sound/imp/output_openal.h | 6 ++-- sound/imp/sound_pair.h | 6 ++-- sound/input.h | 6 ++-- sound/sound.h | 6 ++-- sound/tests/audiere_test.cpp | 2 +- sound/tests/ffmpeg_openal_test.cpp | 2 +- 15 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Doxyfile b/Doxyfile index 4dfff9188..705b0aa32 100644 --- a/Doxyfile +++ b/Doxyfile @@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. -PROJECT_NAME = GOOI +PROJECT_NAME = Mangle # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or diff --git a/LICENSE.txt b/LICENSE.txt index 1b98eeb9b..ccfcc9f22 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Game-oriented object interfaces (GOOI) is licensed under the +Minimal Abstraction Game Layer (Mangle) is licensed under the 'zlib/libpng' license: ---- diff --git a/README.txt b/README.txt index 0b0befca5..2a0d579ec 100644 --- a/README.txt +++ b/README.txt @@ -1,21 +1,21 @@ -Welcome to GOOI v0.1 --------------------- +Welcome to Mangle v0.1 +---------------------- Written by: Nicolay Korslund (korslund@gmail.com) License: zlib/png (see LICENSE.txt) -WWW: http://asm-soft.com/gooi/ -Documentation: http://asm-soft.com/gooi/docs +WWW: http://asm-soft.com/mangle/ +Documentation: http://asm-soft.com/mangle/docs -GOOI stands for Game-Oriented Object Interfaces. It is meant to become -a small set of generic interfaces for various game middleware +Mangle stands for Minimal Abstraction Game Layer, and it's meant to +become a small set of generic interfaces for various game middleware libraries, such as sound, input, graphics, and so on. It consists of several independent modules, one for each of these areas. These may be used together to build an entire game engine, or they can be used individually as separate libraries. -However, GOOI does NOT actually implement a game engine, or any new +However, Mangle does NOT actually implement a game engine, or any new fundamental functionality. More on that below. Currently there is only the Sound module, but more will come in the @@ -53,24 +53,23 @@ possible. What is it good for ------------------- -The main point of GOOI, as we said above, is that it connects to any +The main point of Mangle, as we said above, is that it connects to any library of your choice "behind the scenes" but provides the same, super-simple interface front-end for all of them. There can benefit you in many ways: -- If you want to use a new library that GOOI support. You don't - have to scour the net for tutorials and usage examples, since much - of the common usage code is already included in the implementation - classes. +- If you want to use a new library that Mangle support. You don't have + to scour the net for tutorials and usage examples, since much of the + common usage code is already included in the implementation classes. - If you don't want to pollute your code with library-specific code. - The GOOI interfaces can help you keep your code clean, and its user - interface is often simpler than the exteral library one. + The Mangle interfaces can help you keep your code clean, and its + user interface is often simpler than the exteral library one. - If you are creating a library that depends on a specific feature (such as sound), but you don't want to lock your users into any - specific sound library. GOOI works as an abstraction that lets your - users select their own implementation. My own Monster scripting + specific sound library. Mangle works as an abstraction that lets + your users select their own implementation. My own Monster scripting language ( http://monsterscript.net ) will use this tactic, to provide native-but-generic sound, input and GUI support, among other features. @@ -82,7 +81,7 @@ you in many ways: that your favorite backend doesn't work on all the platforms you want to reach. -The GOOI implementations are extremely light-weight - often just one +The Mangle implementations are extremely light-weight - often just one or two cpp/h pairs. You plug them directly into your program, there's no separate build step required. @@ -95,22 +94,23 @@ come crashing down, because there is no big 'system' to speak of. Past and future --------------- -GOOI started out as a spin-off from OpenMW, another project of mine +Mangle started out as a spin-off from OpenMW, another project of mine ( http://openmw.sourceforge.net ). OpenMW is an attempt to recreate the engine behind the commercial game Morrowind, using only open source software. The projects are still tightly interlinked, and the will continue to be until OpenMW is finished. That means that all near-future work on -GOOI for my part will be more or less guided by what OpenMW needs. But -I'll gladly accept external contributions that are not OpenMW-related. +Mangle for my part will be more or less guided by what OpenMW +needs. But I'll gladly accept external contributions that are not +OpenMW-related. Conclusion ---------- -As you might have guessed, GOOI is more a concept in development than -a finished library right now. +As you might have guessed, Mangle is more a concept in development +than a finished library right now. All feedback, ideas, concepts, questions and code are very welcome. Send them to: korslund@gmail.com diff --git a/sound/imp/audiere_imp.cpp b/sound/imp/audiere_imp.cpp index ea094409f..266fcb2e2 100644 --- a/sound/imp/audiere_imp.cpp +++ b/sound/imp/audiere_imp.cpp @@ -18,7 +18,7 @@ static void fail(const std::string &msg) } using namespace audiere; -using namespace GOOI::Sound; +using namespace Mangle::Sound; AudiereManager::AudiereManager() { diff --git a/sound/imp/audiere_imp.h b/sound/imp/audiere_imp.h index 9f667a98a..083e33258 100644 --- a/sound/imp/audiere_imp.h +++ b/sound/imp/audiere_imp.h @@ -1,12 +1,12 @@ -#ifndef GOOI_SOUND_AUDIERE_H -#define GOOI_SOUND_AUDIERE_H +#ifndef MANGLE_SOUND_AUDIERE_H +#define MANGLE_SOUND_AUDIERE_H #include "../sound.h" #include #include -namespace GOOI { +namespace Mangle { namespace Sound { /// Implementation of Sound::Manager for Audiere diff --git a/sound/imp/input_ffmpeg.cpp b/sound/imp/input_ffmpeg.cpp index c73689b78..9ce822444 100644 --- a/sound/imp/input_ffmpeg.cpp +++ b/sound/imp/input_ffmpeg.cpp @@ -1,7 +1,7 @@ #include "input_ffmpeg.h" #include -using namespace GOOI::Sound; +using namespace Mangle::Sound; // Static output buffer. Not thread safe, but supports multiple // streams operated from the same thread. diff --git a/sound/imp/input_ffmpeg.h b/sound/imp/input_ffmpeg.h index 17c8f7352..8c19d381e 100644 --- a/sound/imp/input_ffmpeg.h +++ b/sound/imp/input_ffmpeg.h @@ -1,5 +1,5 @@ -#ifndef GOOI_SOUND_FFMPEG_H -#define GOOI_SOUND_FFMPEG_H +#ifndef MANGLE_SOUND_FFMPEG_H +#define MANGLE_SOUND_FFMPEG_H #include "../input.h" #include @@ -11,7 +11,7 @@ extern "C" #include } -namespace GOOI { +namespace Mangle { namespace Sound { /// FFmpeg exception diff --git a/sound/imp/openal_ffmpeg.h b/sound/imp/openal_ffmpeg.h index 92fd32553..7e41fd8b9 100644 --- a/sound/imp/openal_ffmpeg.h +++ b/sound/imp/openal_ffmpeg.h @@ -1,11 +1,11 @@ -#ifndef GOOI_FFMPEG_OPENAL_H -#define GOOI_FFMPEG_OPENAL_H +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H #include "sound_pair.h" #include "input_ffmpeg.h" #include "output_openal.h" -namespace GOOI { +namespace Mangle { namespace Sound { /// A PairManager filter that adds FFmpeg decoding to OpenAL diff --git a/sound/imp/output_openal.cpp b/sound/imp/output_openal.cpp index f7dc11507..c56e8c379 100644 --- a/sound/imp/output_openal.cpp +++ b/sound/imp/output_openal.cpp @@ -3,7 +3,7 @@ #include -using namespace GOOI::Sound; +using namespace Mangle::Sound; // ---- Helper functions and classes ---- diff --git a/sound/imp/output_openal.h b/sound/imp/output_openal.h index e2e506aa2..41f801e5a 100644 --- a/sound/imp/output_openal.h +++ b/sound/imp/output_openal.h @@ -1,5 +1,5 @@ -#ifndef GOOI_SOUND_OPENAL_H -#define GOOI_SOUND_OPENAL_H +#ifndef MANGLE_SOUND_OPENAL_H +#define MANGLE_SOUND_OPENAL_H #include "../sound.h" @@ -7,7 +7,7 @@ #include #include -namespace GOOI { +namespace Mangle { namespace Sound { class OpenAL_Stream_Instance; diff --git a/sound/imp/sound_pair.h b/sound/imp/sound_pair.h index 751c72b1a..6b15f7461 100644 --- a/sound/imp/sound_pair.h +++ b/sound/imp/sound_pair.h @@ -1,11 +1,11 @@ -#ifndef GOOI_SOUND_PAIR_H -#define GOOI_SOUND_PAIR_H +#ifndef MANGLE_SOUND_PAIR_H +#define MANGLE_SOUND_PAIR_H #include "sound.h" #include -namespace GOOI { +namespace Mangle { namespace Sound { /** diff --git a/sound/input.h b/sound/input.h index a0d2826aa..b030885fb 100644 --- a/sound/input.h +++ b/sound/input.h @@ -1,10 +1,10 @@ -#ifndef GOOI_SOUND_INPUT_H -#define GOOI_SOUND_INPUT_H +#ifndef MANGLE_SOUND_INPUT_H +#define MANGLE_SOUND_INPUT_H #include #include -namespace GOOI { +namespace Mangle { namespace Sound { /// An abstract interface for a read-once stream of audio data. diff --git a/sound/sound.h b/sound/sound.h index 509efa816..0d1aecf6e 100644 --- a/sound/sound.h +++ b/sound/sound.h @@ -1,10 +1,10 @@ -#ifndef GOOI_SOUND_SOUND_H -#define GOOI_SOUND_SOUND_H +#ifndef MANGLE_SOUND_SOUND_H +#define MANGLE_SOUND_SOUND_H #include #include "input.h" -namespace GOOI { +namespace Mangle { namespace Sound { /// Abstract interface for sound instances diff --git a/sound/tests/audiere_test.cpp b/sound/tests/audiere_test.cpp index 16b132272..defb8c95f 100644 --- a/sound/tests/audiere_test.cpp +++ b/sound/tests/audiere_test.cpp @@ -1,6 +1,6 @@ #include "audiere_imp.h" -using namespace GOOI::Sound; +using namespace Mangle::Sound; AudiereManager mg; diff --git a/sound/tests/ffmpeg_openal_test.cpp b/sound/tests/ffmpeg_openal_test.cpp index 19e34f642..28391af3e 100644 --- a/sound/tests/ffmpeg_openal_test.cpp +++ b/sound/tests/ffmpeg_openal_test.cpp @@ -1,6 +1,6 @@ #include "openal_ffmpeg.h" -using namespace GOOI::Sound; +using namespace Mangle::Sound; OpenAL_FFM_Manager mg; From 325f2f17b35383cbcd2ce0f63c4f99bd1f463ca5 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 21 Nov 2009 11:49:31 +0100 Subject: [PATCH 003/111] Renamed sound_pair to input_filter / InputFilter --- sound/imp/{sound_pair.h => input_filter.h} | 20 ++++++++++---------- sound/imp/openal_ffmpeg.h | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) rename sound/imp/{sound_pair.h => input_filter.h} (79%) diff --git a/sound/imp/sound_pair.h b/sound/imp/input_filter.h similarity index 79% rename from sound/imp/sound_pair.h rename to sound/imp/input_filter.h index 6b15f7461..f81f1e8a5 100644 --- a/sound/imp/sound_pair.h +++ b/sound/imp/input_filter.h @@ -1,5 +1,5 @@ -#ifndef MANGLE_SOUND_PAIR_H -#define MANGLE_SOUND_PAIR_H +#ifndef MANGLE_INPUT_FILTER_H +#define MANGLE_INPUT_FILTER_H #include "sound.h" @@ -19,15 +19,15 @@ namespace Sound { Example: \code - // Combine FFmpeg input and OpenAL output. OpenAL cannot decode - // sound files on its own. - SoundPairManager mg(new FFM_InputManager, new OpenAL_Manager); + // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot + // decode sound files on its own. + InputFilter mg(new OpenAL_Manager, new FFM_InputManager); // We can now load filenames directly. mg.load("file1.mp3"); \endcode */ -class PairManager : public Manager +class InputFilter : public Manager { protected: Manager *snd; @@ -35,14 +35,14 @@ class PairManager : public Manager public: /// Empty constructor - PairManager() {} + InputFilter() {} /// Assign an input manager and a sound manager to this object - PairManager(InputManager *_inp, Manager *_snd) - { set(_inp, _snd); } + InputFilter(Manager *_snd, InputManager *_inp) + { set(_snd, _inp); } /// Assign an input manager and a sound manager to this object - void set(InputManager *_inp, Manager *_snd) + void set(Manager *_snd, InputManager *_inp) { inp = _inp; snd = _snd; diff --git a/sound/imp/openal_ffmpeg.h b/sound/imp/openal_ffmpeg.h index 7e41fd8b9..80882d0f5 100644 --- a/sound/imp/openal_ffmpeg.h +++ b/sound/imp/openal_ffmpeg.h @@ -1,7 +1,7 @@ #ifndef MANGLE_FFMPEG_OPENAL_H #define MANGLE_FFMPEG_OPENAL_H -#include "sound_pair.h" +#include "input_filter.h" #include "input_ffmpeg.h" #include "output_openal.h" @@ -9,13 +9,13 @@ namespace Mangle { namespace Sound { /// A PairManager filter that adds FFmpeg decoding to OpenAL -class OpenAL_FFM_Manager : public PairManager +class OpenAL_FFM_Manager : public InputFilter { public: OpenAL_FFM_Manager() { - set(new FFM_InputManager, - new OpenAL_Manager); + set(new OpenAL_Manager, + new FFM_InputManager); } ~OpenAL_FFM_Manager() { From 7139f5284d5b5d0b87be3774baf3b663a975caf2 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 19 Dec 2009 08:26:01 +0100 Subject: [PATCH 004/111] Added Stream::InputStream interface, and a client implementation to Ogre::DataStream --- README.txt | 6 +-- stream/imp_client/iwrapper.h | 30 +++++++++++++++ stream/imp_client/ogre_datastream.h | 60 +++++++++++++++++++++++++++++ stream/input.h | 50 ++++++++++++++++++++++++ stream/tests/.gitignore | 1 + stream/tests/Makefile | 15 ++++++++ stream/tests/dummy_input.cpp | 48 +++++++++++++++++++++++ stream/tests/dummy_test.cpp | 39 +++++++++++++++++++ stream/tests/ogre_client_test.cpp | 21 ++++++++++ 9 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 stream/imp_client/iwrapper.h create mode 100644 stream/imp_client/ogre_datastream.h create mode 100644 stream/input.h create mode 100644 stream/tests/.gitignore create mode 100644 stream/tests/Makefile create mode 100644 stream/tests/dummy_input.cpp create mode 100644 stream/tests/dummy_test.cpp create mode 100644 stream/tests/ogre_client_test.cpp diff --git a/README.txt b/README.txt index 2a0d579ec..2052d7d7b 100644 --- a/README.txt +++ b/README.txt @@ -18,9 +18,9 @@ individually as separate libraries. However, Mangle does NOT actually implement a game engine, or any new fundamental functionality. More on that below. -Currently there is only the Sound module, but more will come in the -future (including input, 2D/3D graphics, GUI, physics, file -system/archive access, and more.) +Currently there's modules for sound and streams / archives (file +access). More will come in the future (including input, 2D/3D +graphics, GUI, physics, and more.) Main idea diff --git a/stream/imp_client/iwrapper.h b/stream/imp_client/iwrapper.h new file mode 100644 index 000000000..ba2d1fb1f --- /dev/null +++ b/stream/imp_client/iwrapper.h @@ -0,0 +1,30 @@ +#ifndef MANGLE_STREAM_IWRAPPER_H +#define MANGLE_STREAM_IWRAPPER_H + +#include "../input.h" +#include + +namespace Mangle { +namespace Stream { + +/** A generic wrapper class for a Stream::Input object. + + This is used by other implementations. + */ +class _IWrapper +{ + private: + bool autoDel; + + protected: + InputStream *inp; + + public: + _IWrapper(InputStream *_inp, bool _autoDel = false) + : inp(_inp), autoDel(_autoDel) { assert(inp != NULL); } + + virtual ~_IWrapper() { if(autoDel) delete inp; } +}; + +}} // namespaces +#endif diff --git a/stream/imp_client/ogre_datastream.h b/stream/imp_client/ogre_datastream.h new file mode 100644 index 000000000..63ca66e20 --- /dev/null +++ b/stream/imp_client/ogre_datastream.h @@ -0,0 +1,60 @@ +#ifndef MANGLE_STREAM_OGRECLIENT_H +#define MANGLE_STREAM_OGRECLIENT_H + +#include +#include +#include "iwrapper.h" + +namespace Mangle { +namespace Stream { + +/** An OGRE DataStream that wraps a Mangle::Stream input. + + This has been built and tested against OGRE 1.6.2. You might have + to make your own modifications if you're working with newer (or + older) versions. + */ +class MangleDataStream : public Ogre::DataStream, _IWrapper +{ + void init() + { + // Get the size, if possible + if(inp->hasSize) + mSize = inp->size(); + } + + public: + /// Constructor without name + MangleDataStream(InputStream *inp, bool autoDel=false) + : _IWrapper(inp, autoDel) { init(); } + + /// Constructor for a named data stream + MangleDataStream(const Ogre::String &name, InputStream *inp, bool autoDel=false) + : _IWrapper(inp, autoDel), Ogre::DataStream(name) { init(); } + + + // Only implement the DataStream functions we have to implement + + size_t read(void *buf, size_t count) + { return inp->read(buf,count); } + + void skip(long count) + { + assert(inp->isSeekable && inp->hasPosition); + inp->seek(inp->tell() + count); + } + + void seek(size_t pos) + { assert(inp->isSeekable); inp->seek(pos); } + + size_t tell() const + { assert(inp->hasPosition); return inp->tell(); } + + bool eof() const { return inp->eof(); } + + /// Does nothing + void close() {} +}; + +}} // namespaces +#endif diff --git a/stream/input.h b/stream/input.h new file mode 100644 index 000000000..0a178d8fc --- /dev/null +++ b/stream/input.h @@ -0,0 +1,50 @@ +#ifndef MANGLE_STREAM_INPUT_H +#define MANGLE_STREAM_INPUT_H + +#include + +namespace Mangle { +namespace Stream { + +/// An abstract interface for a stream data. +class InputStream +{ + public: + // Feature options. These should be set in the constructor. + + /// If true, seek() works + bool isSeekable; + + /// If true, tell() works + bool hasPosition; + + /// If true, size() works + bool hasSize; + + /// Virtual destructor + virtual ~InputStream() {} + + /** Read a given number of bytes from the stream. Returns the actual + number read. If the return value is less than count, then the + stream is empty or an error occured. + */ + virtual size_t read(void* buf, size_t count) = 0; + + /// Seek to an absolute position in this stream. Not all streams are + /// seekable. + virtual void seek(size_t pos) = 0; + + /// Get the current position in the stream. Non-seekable streams are + /// not required to keep track of this. + virtual size_t tell() const = 0; + + /// Return the total size of the stream. For streams where this is + /// not applicable, size() should return zero. + virtual size_t size() const = 0; + + /// Returns true if the stream is empty + virtual bool eof() const = 0; +}; + +}} // namespaces +#endif diff --git a/stream/tests/.gitignore b/stream/tests/.gitignore new file mode 100644 index 000000000..814490404 --- /dev/null +++ b/stream/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/stream/tests/Makefile b/stream/tests/Makefile new file mode 100644 index 000000000..7d7041469 --- /dev/null +++ b/stream/tests/Makefile @@ -0,0 +1,15 @@ +GCC=g++ -I../ -I../imp_client/ + +all: ogre_client_test dummy_test + +I_OGRE=$(shell pkg-config --cflags OGRE) +L_OGRE=$(shell pkg-config --libs OGRE) + +ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/ogre_datastream.h + $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) + +dummy_test: dummy_test.cpp dummy_input.cpp ../input.h + $(GCC) $< -o $@ + +clean: + rm *_test diff --git a/stream/tests/dummy_input.cpp b/stream/tests/dummy_input.cpp new file mode 100644 index 000000000..4625540ec --- /dev/null +++ b/stream/tests/dummy_input.cpp @@ -0,0 +1,48 @@ +// This file is shared between several test programs +#include "input.h" +#include +#include + +using namespace Mangle::Stream; + +// A simple dummy stream +const char _data[12] = "hello world"; + +class DummyInput : public InputStream +{ +private: + int pos; + +public: + DummyInput() : pos(0) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + size_t read(void *buf, size_t count) + { + assert(pos >= 0 && pos <= 11); + if(count+pos > 11) + count = 11-pos; + assert(count <= 11); + + memcpy(buf, _data+pos, count); + pos += count; + + assert(pos >= 0 && pos <= 11); + return count; + } + + void seek(size_t npos) + { + if(npos > 11) npos = 11; + pos = npos; + } + + size_t tell() const { return pos; } + size_t size() const { return 11; } + + bool eof() const { return pos == 11; } +}; diff --git a/stream/tests/dummy_test.cpp b/stream/tests/dummy_test.cpp new file mode 100644 index 000000000..64dd8f19b --- /dev/null +++ b/stream/tests/dummy_test.cpp @@ -0,0 +1,39 @@ +#include "dummy_input.cpp" + +#include +#include + +using namespace std; + +int main() +{ + InputStream *inp = new DummyInput(); + + cout << "Size: " << inp->size() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking...\n"; + inp->seek(3); + cout << "Pos: " << inp->tell() << endl; + char data[12]; + memset(data, 0, 12); + cout << "Reading: " << inp->read(data, 4) << endl; + cout << "Four bytes: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking again...\n"; + inp->seek(33); + cout << "Pos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << "\nSeek to 6\n"; + inp->seek(6); + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + cout << "Over-reading: " << inp->read(data, 200) << endl; + cout << "Result: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + inp->seek(0); + cout << "Finally, reading the entire string: " << inp->read(data,11) << endl; + cout << "Result: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + + return 0; +} diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp new file mode 100644 index 000000000..b6a46ad3b --- /dev/null +++ b/stream/tests/ogre_client_test.cpp @@ -0,0 +1,21 @@ +#include "dummy_input.cpp" +#include "ogre_datastream.h" +#include + +using namespace Ogre; +using namespace std; + +int main() +{ + InputStream *inp = new DummyInput(); + DataStreamPtr p(new MangleDataStream("hello", inp, true)); + cout << "Name: " << p->getName() << endl; + cout << "As string: " << p->getAsString() << endl; + cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; + p->seek(0); + cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; + p->skip(5); + p->skip(-2); + cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; + return 0; +} From bbb44e07bfa1d3cb2161ee4e1c2156ca135d2f45 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 19 Dec 2009 20:53:53 +0100 Subject: [PATCH 005/111] Added VFS+tests, make Ogre::Archive client implementation. --- Doxyfile | 2 +- stream/tests/dummy_input.cpp | 2 +- vfs/imp_client/ogre_archive.cpp | 83 +++++++++++++++++++++++ vfs/imp_client/ogre_archive.h | 53 +++++++++++++++ vfs/imp_client/wrapper.h | 30 +++++++++ vfs/tests/.gitignore | 1 + vfs/tests/Makefile | 15 +++++ vfs/tests/dummy_test.cpp | 40 +++++++++++ vfs/tests/dummy_vfs.cpp | 115 ++++++++++++++++++++++++++++++++ vfs/tests/ogre_client_test.cpp | 39 +++++++++++ vfs/vfs.h | 79 ++++++++++++++++++++++ 11 files changed, 457 insertions(+), 2 deletions(-) create mode 100644 vfs/imp_client/ogre_archive.cpp create mode 100644 vfs/imp_client/ogre_archive.h create mode 100644 vfs/imp_client/wrapper.h create mode 100644 vfs/tests/.gitignore create mode 100644 vfs/tests/Makefile create mode 100644 vfs/tests/dummy_test.cpp create mode 100644 vfs/tests/dummy_vfs.cpp create mode 100644 vfs/tests/ogre_client_test.cpp create mode 100644 vfs/vfs.h diff --git a/Doxyfile b/Doxyfile index 705b0aa32..f2a1c7455 100644 --- a/Doxyfile +++ b/Doxyfile @@ -564,7 +564,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = sound +INPUT = sound stream vfs # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/stream/tests/dummy_input.cpp b/stream/tests/dummy_input.cpp index 4625540ec..0bfe5ad52 100644 --- a/stream/tests/dummy_input.cpp +++ b/stream/tests/dummy_input.cpp @@ -1,5 +1,5 @@ // This file is shared between several test programs -#include "input.h" +#include "../input.h" #include #include diff --git a/vfs/imp_client/ogre_archive.cpp b/vfs/imp_client/ogre_archive.cpp new file mode 100644 index 000000000..c9dccab90 --- /dev/null +++ b/vfs/imp_client/ogre_archive.cpp @@ -0,0 +1,83 @@ +#include "ogre_archive.h" + +#include "../../stream/imp_client/ogre_datastream.h" + +using namespace Mangle::VFS; +using namespace Mangle::Stream; + +Ogre::DataStreamPtr MangleArchive::open(const Ogre::String& filename) const +{ + return Ogre::DataStreamPtr(new MangleDataStream + (filename, vfs->open(filename), true)); +} + +static void fill(Ogre::FileInfoList &out, FileInfoList &in) +{ + int size = in.size(); + out.resize(size); + + for(int i=0; ilist("", recursive, dirs); + Ogre::StringVector *res = new Ogre::StringVector; + + fill(*res, lst); + + return Ogre::StringVectorPtr(res); +} + +Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs) +{ + FileInfoList lst = vfs->list("", recursive, dirs); + Ogre::FileInfoList *res = new Ogre::FileInfoList; + + fill(*res, lst); + + return Ogre::FileInfoListPtr(res); +} + +// Find functions will only work if vfs->hasFind is set. +Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern, + bool recursive, + bool dirs) +{ + assert(vfs->hasFind); + FileInfoList lst = vfs->find(pattern, recursive, dirs); + Ogre::StringVector *res = new Ogre::StringVector; + + fill(*res, lst); + + return Ogre::StringVectorPtr(res); +} + +Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, + bool recursive, + bool dirs) +{ + assert(vfs->hasFind); + FileInfoList lst = vfs->find(pattern, recursive, dirs); + Ogre::FileInfoList *res = new Ogre::FileInfoList; + + fill(*res, lst); + + return Ogre::FileInfoListPtr(res); +} diff --git a/vfs/imp_client/ogre_archive.h b/vfs/imp_client/ogre_archive.h new file mode 100644 index 000000000..f72398a2d --- /dev/null +++ b/vfs/imp_client/ogre_archive.h @@ -0,0 +1,53 @@ +#ifndef MANGLE_VFS_OGRECLIENT_H +#define MANGLE_VFS_OGRECLIENT_H + +#include +#include +#include "wrapper.h" + +namespace Mangle { +namespace VFS { + +/** An OGRE Archive implementation that wraps a Mangle::VFS + filesystem. + + This has been built and tested against OGRE 1.6.2. You might have + to make your own modifications if you're working with newer (or + older) versions. + */ +class MangleArchive : public Ogre::Archive, _Wrapper +{ + public: + /// Constructor without name + MangleArchive(VFS *vfs, const std::string &name, + const std::string &archType = "Mangle", + bool autoDel=false) + : _Wrapper(vfs, autoDel), Ogre::Archive(name, archType) {} + + bool isCaseSensitive() const { return vfs->isCaseSensitive; } + + // These do nothing. You have to load / unload the archive manually. + void load() {} + void unload() {} + + bool exists(const Ogre::String& filename) + { return vfs->isFile(filename); } + + time_t getModifiedTime(const Ogre::String& filename) + { return vfs->stat(filename).time; } + + Ogre::DataStreamPtr open(const Ogre::String& filename) const; + + Ogre::StringVectorPtr list(bool recursive = true, bool dirs = false); + Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false); + + // Find functions will only work if vfs->hasFind is set. + Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true, + bool dirs = false); + Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, + bool recursive = true, + bool dirs = false); +}; + +}} // namespaces +#endif diff --git a/vfs/imp_client/wrapper.h b/vfs/imp_client/wrapper.h new file mode 100644 index 000000000..357bc8b4f --- /dev/null +++ b/vfs/imp_client/wrapper.h @@ -0,0 +1,30 @@ +#ifndef MANGLE_VFS_WRAPPER_H +#define MANGLE_VFS_WRAPPER_H + +#include "../vfs.h" +#include + +namespace Mangle { +namespace VFS { + +/** A generic wrapper class for a VFS::VFS object. + + This is used by other implementations. + */ +class _Wrapper +{ + private: + bool autoDel; + + protected: + VFS *vfs; + + public: + _Wrapper(VFS *_vfs, bool _autoDel = false) + : vfs(_vfs), autoDel(_autoDel) { assert(vfs != NULL); } + + virtual ~_Wrapper() { if(autoDel) delete vfs; } +}; + +}} // namespaces +#endif diff --git a/vfs/tests/.gitignore b/vfs/tests/.gitignore new file mode 100644 index 000000000..814490404 --- /dev/null +++ b/vfs/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile new file mode 100644 index 000000000..b1fb55f13 --- /dev/null +++ b/vfs/tests/Makefile @@ -0,0 +1,15 @@ +GCC=g++ -I../ -I../imp_client/ + +all: dummy_test ogre_client_test + +I_OGRE=$(shell pkg-config --cflags OGRE) +L_OGRE=$(shell pkg-config --libs OGRE) + +ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrapper.h ../imp_client/ogre_archive.h ../imp_client/ogre_archive.cpp + $(GCC) $< ../imp_client/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) + +dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h + $(GCC) $< -o $@ + +clean: + rm *_test diff --git a/vfs/tests/dummy_test.cpp b/vfs/tests/dummy_test.cpp new file mode 100644 index 000000000..e5659d62f --- /dev/null +++ b/vfs/tests/dummy_test.cpp @@ -0,0 +1,40 @@ +#include "dummy_vfs.cpp" + +#include +#include + +using namespace std; + +void print(FileInfo inf) +{ + cout << "name: " << inf.name << endl; + cout << "basename: " << inf.basename << endl; + cout << "isDir: " << inf.isDir << endl; + cout << "size: " << inf.size << endl; + cout << "time: " << inf.time << endl; +} + +void print(FileInfoList lst) +{ + for(int i=0; isize() << endl; + + return 0; +} diff --git a/vfs/tests/dummy_vfs.cpp b/vfs/tests/dummy_vfs.cpp new file mode 100644 index 000000000..d8b259f28 --- /dev/null +++ b/vfs/tests/dummy_vfs.cpp @@ -0,0 +1,115 @@ +// This file is shared between several test programs +#include "vfs.h" +#include +#include + +#include "../../stream/tests/dummy_input.cpp" + +using namespace Mangle::VFS; + +class DummyVFS : public VFS +{ +public: + DummyVFS() + { + hasFind = false; + isCaseSensitive = true; + } + + // We only support opening 'file1' at the moment. + Mangle::Stream::InputStream *open(const std::string &name) + { + assert(name == "file1"); + return new DummyInput(); + } + + bool isFile(const std::string &name) const + { + return (name == "file1" || + name == "dir/file2"); + } + + bool isDir(const std::string &name) const + { + return name == "dir"; + } + + /// Get info about a single file + FileInfo stat(const std::string &name) const + { + FileInfo fi; + fi.name = name; + fi.time = 0; + + if(isFile(name)) + { + if(name == "dir/file2") + { + fi.basename = "file2"; + fi.size = 2; + } + else + { + fi.basename = "file1"; + fi.size = 1; + } + fi.isDir = false; + } + else if(isDir(name)) + { + fi.basename = "dir"; + fi.isDir = true; + fi.size = 0; + } + else assert(0); + + return fi; + } + + /// List all entries in a given directory. A blank dir should be + /// interpreted as a the root/current directory of the archive. If + /// dirs is true, list directories instead of files. + virtual FileInfoList list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const + { + assert(dir == ""); + + FileInfoList fl; + + FileInfo fi; + + if(!dirs) + { + fi.name = "file1"; + fi.basename = "file1"; + fi.isDir = false; + fi.size = 1; + fi.time = 0; + fl.push_back(fi); + + if(recurse) + { + fi.name = "dir/file2"; + fi.basename = "file2"; + fi.size = 2; + fl.push_back(fi); + } + } + else + { + fi.name = "dir"; + fi.basename = "dir"; + fi.isDir = true; + fi.size = 0; + fi.time = 0; + fl.push_back(fi); + } + return fl; + } + + FileInfoList find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const + { assert(0); return FileInfoList(); } +}; diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp new file mode 100644 index 000000000..92cb73eaa --- /dev/null +++ b/vfs/tests/ogre_client_test.cpp @@ -0,0 +1,39 @@ +#include "dummy_vfs.cpp" +#include "ogre_archive.h" +#include + +using namespace Ogre; +using namespace std; + +void print(StringVectorPtr lst) +{ + int s = lst->size(); + + for(int i=0; isize() << endl; + cout << "contents: " << file->getAsString() << endl; + + return 0; +} diff --git a/vfs/vfs.h b/vfs/vfs.h new file mode 100644 index 000000000..345b4ccae --- /dev/null +++ b/vfs/vfs.h @@ -0,0 +1,79 @@ +#ifndef MANGLE_VFS_H +#define MANGLE_VFS_H + +#include "../stream/input.h" +#include +#include + +namespace Mangle { +namespace VFS { + +/// Generic file info structure +struct FileInfo +{ + /// Full name, including path + std::string name; + + /// Base name, not including path + std::string basename; + + /// Is this a directory? + bool isDir; + + /// File size + size_t size; + + /// Last modification date + time_t time; +}; + +typedef std::vector FileInfoList; + +/** An interface to any file system or other provider of named data + streams +*/ +class VFS +{ + public: + // Feature options. These should be set in the constructor. + + /// If true, the find*() functions work + bool hasFind; + + /// If true, the file system is case sensitive + bool isCaseSensitive; + + /// Virtual destructor + virtual ~VFS() {} + + /// Open a new data stream. Deleting the object should be enough to + /// close it. + virtual Stream::InputStream *open(const std::string &name) = 0; + + /// Check for the existence of a file + virtual bool isFile(const std::string &name) const = 0; + + /// Check for the existence of a directory + virtual bool isDir(const std::string &name) const = 0; + + /// Get info about a single file + virtual FileInfo stat(const std::string &name) const = 0; + + /// List all entries in a given directory. A blank dir should be + /// interpreted as a the root/current directory of the archive. If + /// dirs is true, list directories instead of files. + virtual FileInfoList list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const = 0; + + /// Find files after a given pattern. Wildcards (*) are + /// supported. Only valid if 'hasFind' is true. Don't implement your + /// own pattern matching here if the backend doesn't support it + /// natively; use a filter instead. + virtual FileInfoList find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const = 0; +}; + +}} // namespaces +#endif From dc0b7368461361db21ec7ca94b4f380f7c6dcee6 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 21 Dec 2009 21:51:57 +0100 Subject: [PATCH 006/111] Created separate Audiere sound input class --- sound/imp/input_audiere.cpp | 119 ++++++++++++++++++++++++++++ sound/imp/input_audiere.h | 52 ++++++++++++ sound/imp/openal_audiere.h | 29 +++++++ sound/imp/openal_ffmpeg.h | 2 +- sound/tests/Makefile | 6 +- sound/tests/openal_audiere_test.cpp | 7 ++ 6 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 sound/imp/input_audiere.cpp create mode 100644 sound/imp/input_audiere.h create mode 100644 sound/imp/openal_audiere.h create mode 100644 sound/tests/openal_audiere_test.cpp diff --git a/sound/imp/input_audiere.cpp b/sound/imp/input_audiere.cpp new file mode 100644 index 000000000..5ca85ffd9 --- /dev/null +++ b/sound/imp/input_audiere.cpp @@ -0,0 +1,119 @@ +#include "input_audiere.h" +#include + +// Exception handling +class Audiere_Exception : public std::exception +{ + std::string msg; + + public: + + Audiere_Exception(const std::string &m) : msg(m) {} + ~Audiere_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw Audiere_Exception("Audiere exception: " + msg); +} + +using namespace audiere; +using namespace Mangle::Sound; + +// --- InputManager --- + +InputSource *AudiereInput::load(const std::string &file) +{ return new AudiereSource(file); } + +// --- InputSource --- + +AudiereSource::AudiereSource(const std::string &file) +{ + SampleSourcePtr sample = OpenSampleSource(file.c_str()); + if(!sample) + fail("Couldn't load file " + file); + + buf = CreateSampleBuffer(sample); +} + +InputStream *AudiereSource::getStream() +{ + return new AudiereStream(buf->openStream()); +} + +// --- InputStream --- + +AudiereStream::AudiereStream(SampleSourcePtr _sample) + : sample(_sample), pullSize(0) +{ + assert(sample); + + SampleFormat fmt; + int channels, rate; + sample->getFormat(channels, rate, fmt); + + // Calculate the size of one frame + frameSize = GetSampleSize(fmt) * channels; + + // Make sure that our pullover hack will work. Increase this size if + // this doesn't work in all cases. + assert(frameSize <= PSIZE); +} + +void AudiereStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + SampleFormat fmt; + sample->getFormat(*channels, *rate, fmt); + if(fmt == SF_U8) + *bits = 8; + else if(fmt == SF_S16) + *bits = 16; + else assert(0); +} + +/* + Get data. Since Audiere operates with frames, not bytes, there's a + little conversion magic going on here. We need to make sure we're + reading a whole number of frames - if not, we need to store the + remainding part of the last frame and remember it for the next read + operation. + + */ +uint32_t AudiereStream::getData(void *_data, uint32_t length) +{ + char *data = (char*)_data; + + // Move the remains from the last operation first + if(pullSize) + { + // pullSize is how much was stored the last time, so skip that. + memcpy(data, pullOver+pullSize, PSIZE-pullSize); + length -= pullSize; + data += pullSize; + } + + // Determine the overshoot up front + pullSize = length % frameSize; + + // Number of whole frames + int frames = length / frameSize; + + // Read the data + int res = sample->read(frames, data); + + // Are we missing data? If resread(1, pullOver) != 0)) + { + // Now, move as much of it as we can fit into the output + // data + memcpy(data+length-pullSize, pullOver, pullSize); + } + else pullSize = 0; + + // Return the total number of bytes stored + return frameSize*res + pullSize; +} diff --git a/sound/imp/input_audiere.h b/sound/imp/input_audiere.h new file mode 100644 index 000000000..5acc1a73d --- /dev/null +++ b/sound/imp/input_audiere.h @@ -0,0 +1,52 @@ +#ifndef MANGLE_SOUND_AUDIERE_INPUT_H +#define MANGLE_SOUND_AUDIERE_INPUT_H + +#include "../input.h" + +#include + +namespace Mangle { +namespace Sound { + +/// Implementation of Sound::InputManager for Audiere +class AudiereInput : public InputManager +{ + public: + InputSource *load(const std::string &file); +}; + +/// Audiere InputSource implementation +class AudiereSource : public InputSource +{ + audiere::SampleBufferPtr buf; + + public: + AudiereSource(const std::string &file); + InputStream *getStream(); + void drop() { delete this; } +}; + +/// Audiere InputStream implementation +class AudiereStream : public InputStream +{ + audiere::SampleSourcePtr sample; + int frameSize; // Size of one frame, in bytes + + static const int PSIZE = 10; + + // Temporary storage for unevenly read samples. See the comment for + // getData() in the .cpp file. + char pullOver[PSIZE]; + // How much of the above buffer is in use + int pullSize; + + public: + AudiereStream(audiere::SampleSourcePtr _sample); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + uint32_t getData(void *data, uint32_t length); + void drop() { delete this; } +}; + +}} // Namespace +#endif diff --git a/sound/imp/openal_audiere.h b/sound/imp/openal_audiere.h new file mode 100644 index 000000000..65947b22f --- /dev/null +++ b/sound/imp/openal_audiere.h @@ -0,0 +1,29 @@ +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H + +#include "input_filter.h" +#include "input_audiere.h" +#include "output_openal.h" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds audiere decoding to OpenAL. Audiere has +/// it's own output, but OpenAL sports 3D and other advanced features. +class OpenAL_Audiere_Manager : public InputFilter +{ + public: + OpenAL_Audiere_Manager() + { + set(new OpenAL_Manager, + new AudiereInput); + } + ~OpenAL_Audiere_Manager() + { + delete snd; + delete inp; + } +}; + +}} +#endif diff --git a/sound/imp/openal_ffmpeg.h b/sound/imp/openal_ffmpeg.h index 80882d0f5..179d3cb70 100644 --- a/sound/imp/openal_ffmpeg.h +++ b/sound/imp/openal_ffmpeg.h @@ -8,7 +8,7 @@ namespace Mangle { namespace Sound { -/// A PairManager filter that adds FFmpeg decoding to OpenAL +/// An InputFilter that adds FFmpeg decoding to OpenAL class OpenAL_FFM_Manager : public InputFilter { public: diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 4cdf5ab58..22da35432 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,15 +1,17 @@ GCC=g++ -I../ -I../imp/ -all: audiere_test ffmpeg_openal_test +all: audiere_test ffmpeg_openal_test openal_audiere_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) - L_AUDIERE=-laudiere ffmpeg_openal_test: ffmpeg_openal_test.cpp ../imp/input_ffmpeg.cpp ../imp/output_openal.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) +openal_audiere_test: openal_audiere_test.cpp ../imp/input_audiere.cpp ../imp/output_openal.cpp + $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) + audiere_test: audiere_test.cpp ../imp/audiere_imp.cpp $(GCC) $^ -o $@ $(L_AUDIERE) diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp new file mode 100644 index 000000000..c9011f48e --- /dev/null +++ b/sound/tests/openal_audiere_test.cpp @@ -0,0 +1,7 @@ +#include "openal_audiere.h" + +using namespace Mangle::Sound; + +OpenAL_Audiere_Manager mg; + +#include "common.cpp" From 721f3b139bd4779230fa01d453b0871a8076e5f7 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 22 Dec 2009 13:04:44 +0100 Subject: [PATCH 007/111] Added Stream input method to sound interfaces (not implemented, NOT TESTED YET) --- sound/imp/audiere_imp.cpp | 1 + sound/imp/audiere_imp.h | 4 ++++ sound/imp/input_ffmpeg.cpp | 2 ++ sound/imp/input_ffmpeg.h | 3 +++ sound/imp/input_filter.h | 37 +++++++++++++++++++++---------------- sound/imp/output_openal.cpp | 3 +++ sound/imp/output_openal.h | 1 + sound/input.h | 8 ++++++++ sound/sound.h | 15 +++++++++++++++ 9 files changed, 58 insertions(+), 16 deletions(-) diff --git a/sound/imp/audiere_imp.cpp b/sound/imp/audiere_imp.cpp index 266fcb2e2..ecdb0580a 100644 --- a/sound/imp/audiere_imp.cpp +++ b/sound/imp/audiere_imp.cpp @@ -27,6 +27,7 @@ AudiereManager::AudiereManager() canRepeatStream = true; canLoadFile = true; canLoadSource = false; + canLoadStream = false; device = OpenDevice(""); diff --git a/sound/imp/audiere_imp.h b/sound/imp/audiere_imp.h index 083e33258..480611ce3 100644 --- a/sound/imp/audiere_imp.h +++ b/sound/imp/audiere_imp.h @@ -19,6 +19,10 @@ class AudiereManager : public Manager virtual Sound *load(const std::string &file, bool stream=false); + /// not implemented yet + virtual Sound *load(Stream::InputStream *input, bool stream=false) + { assert(0); } + /// disabled virtual Sound *load(InputSource *input, bool stream=false) { assert(0); } diff --git a/sound/imp/input_ffmpeg.cpp b/sound/imp/input_ffmpeg.cpp index 9ce822444..f29ddfdb6 100644 --- a/sound/imp/input_ffmpeg.cpp +++ b/sound/imp/input_ffmpeg.cpp @@ -32,6 +32,8 @@ FFM_InputManager::FFM_InputManager() av_log_set_level(AV_LOG_ERROR); init = true; } + + canLoadStream = false; } InputSource *FFM_InputManager::load(const std::string &file) diff --git a/sound/imp/input_ffmpeg.h b/sound/imp/input_ffmpeg.h index 8c19d381e..cc8feffd4 100644 --- a/sound/imp/input_ffmpeg.h +++ b/sound/imp/input_ffmpeg.h @@ -34,6 +34,9 @@ class FFM_InputManager : public InputManager public: FFM_InputManager(); virtual InputSource *load(const std::string &file); + + /// not supported + virtual InputSource *load(Stream::InputStream *input) { assert(0); } }; /// FFMpeg implementation of InputSource diff --git a/sound/imp/input_filter.h b/sound/imp/input_filter.h index f81f1e8a5..c3da8599d 100644 --- a/sound/imp/input_filter.h +++ b/sound/imp/input_filter.h @@ -43,26 +43,31 @@ class InputFilter : public Manager /// Assign an input manager and a sound manager to this object void set(Manager *_snd, InputManager *_inp) - { - inp = _inp; - snd = _snd; - - needsUpdate = snd->needsUpdate; - has3D = snd->has3D; - canRepeatStream = snd->canRepeatStream; - - // Both these should be true, or the use of this class is pretty - // pointless - canLoadSource = snd->canLoadSource; - canLoadFile = canLoadSource; - assert(canLoadSource && canLoadFile); - } + { + inp = _inp; + snd = _snd; + + // Set capabilities + needsUpdate = snd->needsUpdate; + has3D = snd->has3D; + canRepeatStream = snd->canRepeatStream; + canLoadStream = inp->canLoadStream; + + // Both these should be true, or the use of this class is pretty + // pointless + canLoadSource = snd->canLoadSource; + canLoadFile = canLoadSource; + assert(canLoadSource && canLoadFile); + } virtual Sound *load(const std::string &file, bool stream=false) - { return load(inp->load(file), stream); } + { return load(inp->load(file), stream); } + + virtual Sound *load(Stream::InputStream *input, bool stream=false) + { return load(inp->load(input), stream); } virtual Sound *load(InputSource *input, bool stream=false) - { return snd->load(input, stream); } + { return snd->load(input, stream); } virtual void update() { snd->update(); } virtual void setListenerPos(float x, float y, float z, diff --git a/sound/imp/output_openal.cpp b/sound/imp/output_openal.cpp index c56e8c379..190d0638d 100644 --- a/sound/imp/output_openal.cpp +++ b/sound/imp/output_openal.cpp @@ -97,6 +97,9 @@ OpenAL_Manager::~OpenAL_Manager() Sound *OpenAL_Manager::load(const std::string &file, bool stream) { assert(0 && "OpenAL cannot decode files"); } +Sound *OpenAL_Manager::load(Stream::InputStream*,bool) +{ assert(0 && "OpenAL cannot decode streams"); } + Sound *OpenAL_Manager::load(InputSource *source, bool stream) { return new OpenAL_Sound(source, this, stream); } diff --git a/sound/imp/output_openal.h b/sound/imp/output_openal.h index 41f801e5a..bf92197df 100644 --- a/sound/imp/output_openal.h +++ b/sound/imp/output_openal.h @@ -26,6 +26,7 @@ public: void remove_stream(LST::iterator); virtual Sound *load(const std::string &file, bool stream=false); + virtual Sound *load(Stream::InputStream *input, bool stream=false); virtual Sound *load(InputSource* input, bool stream=false); virtual void update(); virtual void setListenerPos(float x, float y, float z, diff --git a/sound/input.h b/sound/input.h index b030885fb..d27de97a6 100644 --- a/sound/input.h +++ b/sound/input.h @@ -4,6 +4,8 @@ #include #include +#include "../stream/input.h" + namespace Mangle { namespace Sound { @@ -64,9 +66,15 @@ class InputSource class InputManager { public: + /// If true, the stream version of load() works + bool canLoadStream; + /// Load a sound input source from file virtual InputSource *load(const std::string &file) = 0; + /// Load a sound input source from stream (if canLoadStream is true) + virtual InputSource *load(Stream::InputStream *input) = 0; + /// Virtual destructor virtual ~InputManager() {} }; diff --git a/sound/sound.h b/sound/sound.h index 0d1aecf6e..90407141e 100644 --- a/sound/sound.h +++ b/sound/sound.h @@ -4,6 +4,8 @@ #include #include "input.h" +#include "../stream/input.h" + namespace Mangle { namespace Sound { @@ -115,6 +117,9 @@ class Manager /// true if we can load sounds from an InputSource bool canLoadSource; + /// If true, we can lound sound files from a Stream + bool canLoadStream; + /** @brief Load a sound from an input source. Only valid if canLoadSource is true. @@ -132,6 +137,16 @@ class Manager */ virtual Sound *load(InputSource *input, bool stream=false) = 0; + /** + @brief Load a sound directly from file. Only valid if canLoadStream + is true. + + @param input audio file stream + @param stream true if the file should be streamed + @see load(InputSource*,bool) + */ + virtual Sound *load(Stream::InputStream *input, bool stream=false) = 0; + /** @brief Load a sound directly from file. Only valid if canLoadFile is true. From 985e3847e0861bb39dfc277d8fb1afebf3190c89 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 22 Dec 2009 13:22:32 +0100 Subject: [PATCH 008/111] Added audiere File client. NOT TESTED. --- stream/imp_client/audiere_file.cpp | 39 ++++++++++++++++++++++++++++++ stream/imp_client/audiere_file.h | 35 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 stream/imp_client/audiere_file.cpp create mode 100644 stream/imp_client/audiere_file.h diff --git a/stream/imp_client/audiere_file.cpp b/stream/imp_client/audiere_file.cpp new file mode 100644 index 000000000..ea8142eb4 --- /dev/null +++ b/stream/imp_client/audiere_file.cpp @@ -0,0 +1,39 @@ +#include "audiere_file.h" + +using namespace audiere; +using namespace Mangle::Stream; + +bool AudiereFile::seek(int pos, SeekMode mode) +{ + assert(inp->isSeekable); + assert(inp->hasPosition); + + if(mode == BEGIN) + { + // Absolute position + inp->seek(pos); + return inp->tell() == pos; + } + if(mode == CURRENT) + { + // Current position + int cpos = inp->tell(); + + // Seek to a elative position + inp->seek(cpos + pos); + return inp->tell() == (pos+cpos); + } + if(mode == END) + { + // Seeking from the end. This requires that we're able to get + // the entire size of the file. The pos also has to be + // non-positive. + assert(inp->hasSize); + assert(pos <= 0); + + size_t epos = inp->size(); + inp->seek(epos + pos); + return inp->tell() == (epos+pos); + } + assert(0 && "invalid seek mode"); +} diff --git a/stream/imp_client/audiere_file.h b/stream/imp_client/audiere_file.h new file mode 100644 index 000000000..201a7629a --- /dev/null +++ b/stream/imp_client/audiere_file.h @@ -0,0 +1,35 @@ +#ifndef MANGLE_STREAM_AUDIERECLIENT_H +#define MANGLE_STREAM_AUDIERECLIENT_H + +#include +#include +#include "iwrapper.h" + +namespace Mangle { +namespace Stream { + +/** @brief An Audiere::File that wraps a Mangle::Stream input. + + This lets Audiere read sound files from any generic archive or + file manager that supports Mangle streams. + */ +class AudiereFile : public audiere::File, _IWrapper +{ + public: + AudiereFile(InputStream *inp, bool autoDel=false) + : _IWrapper(inp, autoDel) {} + + /// Read 'count' bytes, return bytes successfully read + int read(void *buf, int count) + { return inp->read(buf,count); } + + /// Seek, relative to specified seek mode. Returns true if successful. + bool seek(int pos, audiere::SeekMode mode); + + /// Get current position + int tell() + { assert(inp->hasPosition); return inp->tell(); } +}; + +}} // namespaces +#endif From 008b4c64aaf348b34224f69bc248df27267144fe Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 22 Dec 2009 16:58:19 +0100 Subject: [PATCH 009/111] Added OGRE server prototype - NOT DONE --- stream/imp_server/ogre_datastream.h | 35 ++++++++++++++++ vfs/imp_client/ogre_archive.h | 1 - vfs/imp_server/ogre_vfs.h | 62 +++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 stream/imp_server/ogre_datastream.h create mode 100644 vfs/imp_server/ogre_vfs.h diff --git a/stream/imp_server/ogre_datastream.h b/stream/imp_server/ogre_datastream.h new file mode 100644 index 000000000..ef922fa7f --- /dev/null +++ b/stream/imp_server/ogre_datastream.h @@ -0,0 +1,35 @@ +#ifndef MANGLE_STREAM_OGRESERVER_H +#define MANGLE_STREAM_OGRESERVER_H + +#include + +namespace Mangle { +namespace Stream { + +/** A Stream wrapping an OGRE DataStream. + + This has been built and tested against OGRE 1.6.2. You might have + to make your own modifications if you're working with newer (or + older) versions. + */ +class OgreStream : public InputStream +{ + Ogre::DataStreamPtr inp; + + public: + OgreStream(Ogre::DataStreamPtr _inp) : inp(_inp) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + size_t read(void *buf, size_t count) { return inp->read(buf,count); } + void seek(size_t pos) { inp->seek(pos); } + size_t tell() const { return inp->tell(); } + size_t size() const { return inp->size(); } + bool eof() const { return inp->eof(); } +}; + +}} // namespaces +#endif diff --git a/vfs/imp_client/ogre_archive.h b/vfs/imp_client/ogre_archive.h index f72398a2d..70e6045c4 100644 --- a/vfs/imp_client/ogre_archive.h +++ b/vfs/imp_client/ogre_archive.h @@ -18,7 +18,6 @@ namespace VFS { class MangleArchive : public Ogre::Archive, _Wrapper { public: - /// Constructor without name MangleArchive(VFS *vfs, const std::string &name, const std::string &archType = "Mangle", bool autoDel=false) diff --git a/vfs/imp_server/ogre_vfs.h b/vfs/imp_server/ogre_vfs.h new file mode 100644 index 000000000..98cf5da79 --- /dev/null +++ b/vfs/imp_server/ogre_vfs.h @@ -0,0 +1,62 @@ +#ifndef MANGLE_VFS_OGRECLIENT_H +#define MANGLE_VFS_OGRECLIENT_H + +#include + +namespace Mangle { +namespace VFS { + +/** @brief An interface into the OGRE VFS system. + + This class does not wrap a single Ogre::Archive, but rather the + entire resource set available to Ogre. You can use this class to + tap into all paths, Zip files, custom archives on so on that have + been inserted into Ogre as resource locations. + + This class is currently a work in progres, it will not compile as + it stands. + + This has been built and tested against OGRE 1.6.2. You might have + to make your own modifications if you're working with newer (or + older) versions. + */ +class OgreVFS : public VFS +{ + public: + OgreVFS() + { + hasFind = true; + isCaseSensitive = true; + } + + /// Open a new data stream. Deleting the object should be enough to + /// close it. + virtual Stream::InputStream *open(const std::string &name); + + /// Check for the existence of a file + virtual bool isFile(const std::string &name) const; + + /// Check for the existence of a directory + virtual bool isDir(const std::string &name) const; + + /// Get info about a single file + virtual FileInfo stat(const std::string &name) const; + + /// List all entries in a given directory. A blank dir should be + /// interpreted as a the root/current directory of the archive. If + /// dirs is true, list directories instead of files. + virtual FileInfoList list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const; + + /// Find files after a given pattern. Wildcards (*) are + /// supported. Only valid if 'hasFind' is true. Don't implement your + /// own pattern matching here if the backend doesn't support it + /// natively; use a filter instead (not implemented yet.) + virtual FileInfoList find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const; +}; + +}} // namespaces +#endif From c54301fc1c2e15010d21d98a7dfb45b9d492686f Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 24 Dec 2009 11:08:18 +0100 Subject: [PATCH 010/111] Finished merge, created AudiereFile test --- sound/imp/input_audiere.cpp | 8 +++++++ sound/imp/input_audiere.h | 6 +++++ sound/imp/input_ffmpeg.h | 1 + sound/imp/output_openal.cpp | 1 + stream/imp_client/audiere_file.h | 4 ++-- stream/tests/Makefile | 6 ++++- stream/tests/audiere_client_test.cpp | 33 ++++++++++++++++++++++++++++ 7 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 stream/tests/audiere_client_test.cpp diff --git a/sound/imp/input_audiere.cpp b/sound/imp/input_audiere.cpp index 5ca85ffd9..624b2050b 100644 --- a/sound/imp/input_audiere.cpp +++ b/sound/imp/input_audiere.cpp @@ -23,9 +23,17 @@ using namespace Mangle::Sound; // --- InputManager --- +AudiereInput::AudiereInput() +{ + canLoadStream = false; +} + InputSource *AudiereInput::load(const std::string &file) { return new AudiereSource(file); } +InputSource *AudiereInput::load(Stream::InputStream *input) +{ assert(0 && "not implemented yet"); } + // --- InputSource --- AudiereSource::AudiereSource(const std::string &file) diff --git a/sound/imp/input_audiere.h b/sound/imp/input_audiere.h index 5acc1a73d..344c07735 100644 --- a/sound/imp/input_audiere.h +++ b/sound/imp/input_audiere.h @@ -12,7 +12,13 @@ namespace Sound { class AudiereInput : public InputManager { public: + AudiereInput(); + + /// Load a source from a file InputSource *load(const std::string &file); + + /// Load a source from a stream + virtual InputSource *load(Stream::InputStream *input); }; /// Audiere InputSource implementation diff --git a/sound/imp/input_ffmpeg.h b/sound/imp/input_ffmpeg.h index cc8feffd4..ae25732b5 100644 --- a/sound/imp/input_ffmpeg.h +++ b/sound/imp/input_ffmpeg.h @@ -4,6 +4,7 @@ #include "../input.h" #include #include +#include extern "C" { diff --git a/sound/imp/output_openal.cpp b/sound/imp/output_openal.cpp index 190d0638d..e36981836 100644 --- a/sound/imp/output_openal.cpp +++ b/sound/imp/output_openal.cpp @@ -75,6 +75,7 @@ OpenAL_Manager::OpenAL_Manager() canRepeatStream = false; canLoadFile = false; canLoadSource = true; + canLoadStream = false; // Set up sound system Device = alcOpenDevice(NULL); diff --git a/stream/imp_client/audiere_file.h b/stream/imp_client/audiere_file.h index 201a7629a..a1910d274 100644 --- a/stream/imp_client/audiere_file.h +++ b/stream/imp_client/audiere_file.h @@ -13,7 +13,7 @@ namespace Stream { This lets Audiere read sound files from any generic archive or file manager that supports Mangle streams. */ -class AudiereFile : public audiere::File, _IWrapper +class AudiereFile : public audiere::RefImplementation, _IWrapper { public: AudiereFile(InputStream *inp, bool autoDel=false) @@ -24,7 +24,7 @@ class AudiereFile : public audiere::File, _IWrapper { return inp->read(buf,count); } /// Seek, relative to specified seek mode. Returns true if successful. - bool seek(int pos, audiere::SeekMode mode); + bool seek(int pos, audiere::File::SeekMode mode); /// Get current position int tell() diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 7d7041469..3d2a730b8 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,13 +1,17 @@ GCC=g++ -I../ -I../imp_client/ -all: ogre_client_test dummy_test +all: ogre_client_test dummy_test audiere_client_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) +L_AUDIERE=-laudiere ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/ogre_datastream.h $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) +audiere_client_test: audiere_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/audiere_file.h ../imp_client/audiere_file.cpp + $(GCC) $< -o $@ ../imp_client/audiere_file.cpp $(L_AUDIERE) + dummy_test: dummy_test.cpp dummy_input.cpp ../input.h $(GCC) $< -o $@ diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp new file mode 100644 index 000000000..7a5b5afc2 --- /dev/null +++ b/stream/tests/audiere_client_test.cpp @@ -0,0 +1,33 @@ +#include "dummy_input.cpp" +#include "../imp_client/audiere_file.h" +#include +#include + +using namespace audiere; +using namespace std; + +int main() +{ + char str[12]; + memset(str, 0, 12); + InputStream *inp = new DummyInput(); + FilePtr p(new AudiereFile(inp, true)); + cout << "pos=" << p->tell() << endl; + p->read(str, 2); + cout << "2 bytes: " << str << endl; + cout << "pos=" << p->tell() << endl; + p->seek(4, File::BEGIN); + cout << "pos=" << p->tell() << endl; + p->read(str, 3); + cout << "3 bytes: " << str << endl; + p->seek(-1, File::CURRENT); + cout << "pos=" << p->tell() << endl; + p->seek(-4, File::END); + cout << "pos=" << p->tell() << endl; + p->read(str, 4); + cout << "last 4 bytes: " << str << endl; + p->seek(0, File::BEGIN); + p->read(str, 11); + cout << "entire stream: " << str << endl; + return 0; +} From b601cdff629ddd53a236ab388f57837672f5fe24 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 10:52:10 +0100 Subject: [PATCH 011/111] Minor changes to VFS. Updated ogre_resource test --- stream/imp_client/audiere_file.cpp | 35 +++++------- vfs/imp_client/ogre_archive.cpp | 2 + vfs/imp_server/ogre_vfs.h | 3 +- vfs/tests/Makefile | 5 +- vfs/tests/ogre_resource_test.cpp | 87 ++++++++++++++++++++++++++++++ vfs/vfs.h | 5 +- 6 files changed, 113 insertions(+), 24 deletions(-) create mode 100644 vfs/tests/ogre_resource_test.cpp diff --git a/stream/imp_client/audiere_file.cpp b/stream/imp_client/audiere_file.cpp index ea8142eb4..53638781e 100644 --- a/stream/imp_client/audiere_file.cpp +++ b/stream/imp_client/audiere_file.cpp @@ -8,32 +8,25 @@ bool AudiereFile::seek(int pos, SeekMode mode) assert(inp->isSeekable); assert(inp->hasPosition); - if(mode == BEGIN) - { - // Absolute position - inp->seek(pos); - return inp->tell() == pos; - } - if(mode == CURRENT) - { - // Current position - int cpos = inp->tell(); + int newPos; - // Seek to a elative position - inp->seek(cpos + pos); - return inp->tell() == (pos+cpos); - } - if(mode == END) + switch(mode) { + case BEGIN: newPos = pos; break; + case CURRENT: newPos = pos+tell(); break; + case END: // Seeking from the end. This requires that we're able to get - // the entire size of the file. The pos also has to be + // the entire size of the stream. The pos also has to be // non-positive. assert(inp->hasSize); assert(pos <= 0); - - size_t epos = inp->size(); - inp->seek(epos + pos); - return inp->tell() == (epos+pos); + newPos = inp->size() + pos; + break; + default: + assert(0 && "invalid seek mode"); } - assert(0 && "invalid seek mode"); + + inp->seek(newPos); + return inp->tell() == newPos; + } diff --git a/vfs/imp_client/ogre_archive.cpp b/vfs/imp_client/ogre_archive.cpp index c9dccab90..d7adaa07d 100644 --- a/vfs/imp_client/ogre_archive.cpp +++ b/vfs/imp_client/ogre_archive.cpp @@ -37,6 +37,7 @@ static void fill(Ogre::StringVector &out, FileInfoList &in) Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs) { + assert(vfs->hasList); FileInfoList lst = vfs->list("", recursive, dirs); Ogre::StringVector *res = new Ogre::StringVector; @@ -47,6 +48,7 @@ Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs) Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs) { + assert(vfs->hasList); FileInfoList lst = vfs->list("", recursive, dirs); Ogre::FileInfoList *res = new Ogre::FileInfoList; diff --git a/vfs/imp_server/ogre_vfs.h b/vfs/imp_server/ogre_vfs.h index 98cf5da79..081211433 100644 --- a/vfs/imp_server/ogre_vfs.h +++ b/vfs/imp_server/ogre_vfs.h @@ -25,7 +25,8 @@ class OgreVFS : public VFS public: OgreVFS() { - hasFind = true; + hasList = false; + hasFind = false; isCaseSensitive = true; } diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index b1fb55f13..135df10c6 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -I../imp_client/ -all: dummy_test ogre_client_test +all: dummy_test ogre_client_test ogre_resource_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -8,6 +8,9 @@ L_OGRE=$(shell pkg-config --libs OGRE) ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrapper.h ../imp_client/ogre_archive.h ../imp_client/ogre_archive.cpp $(GCC) $< ../imp_client/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) +ogre_resource_test: ogre_resource_test.cpp + $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) + dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h $(GCC) $< -o $@ diff --git a/vfs/tests/ogre_resource_test.cpp b/vfs/tests/ogre_resource_test.cpp new file mode 100644 index 000000000..36f84c241 --- /dev/null +++ b/vfs/tests/ogre_resource_test.cpp @@ -0,0 +1,87 @@ +#include +#include +#include + +/* + This isn't really a test of our implementation, but a test of using + the Ogre resource system to find files. If the Ogre interface + changes and you have to change this test, you will have to change + the ogre_vfs.cpp implementation equivalently. + + */ + +using namespace std; +using namespace Ogre; + +ResourceGroupManager *gm; +String group; + +void find(const std::string &fileName) +{ + cout << "\nFile: " << fileName << endl; + + if(!gm->resourceExists(group, fileName)) + { + cout << "Does not exist\n"; + return; + } + + DataStreamPtr data = gm->openResource(fileName, group); + + cout << "Size: " << data->size() << endl; + cout << "First line: " << data->getLine() << "\n"; + + + // Alternative - not used / fixed yet + + /* This won't work, since we don't have access to Ogre + internals. That's a shame. + + LocationList::iterator li, liend; + liend = grp->locationList.end(); + for (li = grp->locationList.begin(); li != liend; ++li) + { + Archive* arch = (*li)->archive; + + // The rest is client code - using an archive. We might make a + // shared implementation, or possibly convert the archives into + // a vfs list at load time (although that isn't very flexible.) + + // Do we perform these searches in each function? I guess we + // have to. + if (arch->exists(resourceName)) + { + DataStreamPtr ptr = arch->open(resourceName); + return ptr; + } + } + */ +} + +int main() +{ + // Disable logging + new LogManager; + Log *log = LogManager::getSingleton().createLog(""); + log->setDebugOutputEnabled(false); + + // Set up Ogre + Root root("","",""); + + root.addResourceLocation("./", "FileSystem", "General"); + + gm = ResourceGroupManager::getSingletonPtr(); + group = gm->getWorldResourceGroupName(); + + find("Makefile"); + find("ogre_resource_test.cpp"); + find("bleh"); + + cout << "\nAll source files:\n"; + FileInfoListPtr list = gm->findResourceFileInfo(group, "*.cpp"); + FileInfoList::iterator it, end; + it = list->begin(); + end = list->end(); + for(; it != end; it++) + cout << " " << it->filename << endl; +} diff --git a/vfs/vfs.h b/vfs/vfs.h index 345b4ccae..a9ffb6b62 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.h @@ -37,7 +37,10 @@ class VFS public: // Feature options. These should be set in the constructor. - /// If true, the find*() functions work + /// If true, the list() function work + bool hasList; + + /// If true, the find() function work bool hasFind; /// If true, the file system is case sensitive From 262cb9255cc54808c17aabb24fc9b230e23f5a59 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 12:13:31 +0100 Subject: [PATCH 012/111] Completed OGRE VFS server +test --- vfs/imp_server/ogre_vfs.cpp | 60 +++++++++++++++++++++++++++++ vfs/imp_server/ogre_vfs.h | 57 ++++++++++++++++------------ vfs/tests/Makefile | 7 +++- vfs/tests/ogre_client_test.cpp | 2 +- vfs/tests/ogre_resource_test.cpp | 26 ------------- vfs/tests/ogre_server_test.cpp | 63 +++++++++++++++++++++++++++++++ vfs/tests/test.zip | Bin 0 -> 169 bytes 7 files changed, 161 insertions(+), 54 deletions(-) create mode 100644 vfs/imp_server/ogre_vfs.cpp create mode 100644 vfs/tests/ogre_server_test.cpp create mode 100644 vfs/tests/test.zip diff --git a/vfs/imp_server/ogre_vfs.cpp b/vfs/imp_server/ogre_vfs.cpp new file mode 100644 index 000000000..9ca1c4b8c --- /dev/null +++ b/vfs/imp_server/ogre_vfs.cpp @@ -0,0 +1,60 @@ +#include "ogre_vfs.h" +#include "../../stream/imp_server/ogre_datastream.h" + +using namespace Mangle::VFS; + +OgreVFS::OgreVFS(const std::string &_group) + : group(_group) +{ + hasList = true; + hasFind = true; + isCaseSensitive = true; + + // Get the group manager once + gm = Ogre::ResourceGroupManager::getSingletonPtr(); + + // Use the default group if none was specified + if(group.empty()) + group = gm->getWorldResourceGroupName(); +} + +Mangle::Stream::InputStream *OgreVFS::open(const std::string &name) +{ + Ogre::DataStreamPtr data = gm->openResource(name, group); + return new Stream::OgreStream(data); +} + +static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) +{ + int size = in.size(); + out.resize(size); + + for(int i=0; ilistResourceFileInfo(group, dirs); + FileInfoList res; + fill(res, *olist, dirs); + return res; +} + +FileInfoList OgreVFS::find(const std::string& pattern, + bool recursive, + bool dirs) const +{ + Ogre::FileInfoListPtr olist = gm->findResourceFileInfo(group, pattern, dirs); + FileInfoList res; + fill(res, *olist, dirs); + return res; +} diff --git a/vfs/imp_server/ogre_vfs.h b/vfs/imp_server/ogre_vfs.h index 081211433..9a01c1786 100644 --- a/vfs/imp_server/ogre_vfs.h +++ b/vfs/imp_server/ogre_vfs.h @@ -1,20 +1,18 @@ -#ifndef MANGLE_VFS_OGRECLIENT_H -#define MANGLE_VFS_OGRECLIENT_H +#ifndef MANGLE_VFS_OGRESERVER_H +#define MANGLE_VFS_OGRESERVER_H -#include +#include "../vfs.h" +#include namespace Mangle { namespace VFS { /** @brief An interface into the OGRE VFS system. - This class does not wrap a single Ogre::Archive, but rather the - entire resource set available to Ogre. You can use this class to - tap into all paths, Zip files, custom archives on so on that have - been inserted into Ogre as resource locations. - - This class is currently a work in progres, it will not compile as - it stands. + This class does NOT wrap a single Ogre::Archive, but rather an + entire resource group in Ogre. You can use this class to tap into + all paths, Zip files, custom archives on so on that have been + inserted into Ogre as resource locations. This has been built and tested against OGRE 1.6.2. You might have to make your own modifications if you're working with newer (or @@ -22,38 +20,47 @@ namespace VFS { */ class OgreVFS : public VFS { + std::string group; + Ogre::ResourceGroupManager *gm; + public: - OgreVFS() - { - hasList = false; - hasFind = false; - isCaseSensitive = true; - } + /** @brief Constructor + + OGRE must be initialized (ie. you must have created an + Ogre::Root object somewhere) before calling this. + + @param group Optional resource group name. If none is given, + OGRE's default (or 'World') resource group is used. + */ + OgreVFS(const std::string &_group = ""); /// Open a new data stream. Deleting the object should be enough to /// close it. virtual Stream::InputStream *open(const std::string &name); /// Check for the existence of a file - virtual bool isFile(const std::string &name) const; + virtual bool isFile(const std::string &name) const + { return gm->resourceExists(group, name); } - /// Check for the existence of a directory - virtual bool isDir(const std::string &name) const; + /// This doesn't work, always returns false. + virtual bool isDir(const std::string &name) const + { return false; } - /// Get info about a single file - virtual FileInfo stat(const std::string &name) const; + /// This doesn't work. + virtual FileInfo stat(const std::string &name) const + { return FileInfo(); } /// List all entries in a given directory. A blank dir should be /// interpreted as a the root/current directory of the archive. If - /// dirs is true, list directories instead of files. + /// dirs is true, list directories instead of files. OGRE note: The + /// ogre resource systemd does not support recursive listing of + /// files. We might make a separate filter for this later. virtual FileInfoList list(const std::string& dir = "", bool recurse=true, bool dirs=false) const; /// Find files after a given pattern. Wildcards (*) are - /// supported. Only valid if 'hasFind' is true. Don't implement your - /// own pattern matching here if the backend doesn't support it - /// natively; use a filter instead (not implemented yet.) + /// supported. virtual FileInfoList find(const std::string& pattern, bool recursive=true, bool dirs=false) const; diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index 135df10c6..77e02f927 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -1,6 +1,6 @@ -GCC=g++ -I../ -I../imp_client/ +GCC=g++ -I../ -all: dummy_test ogre_client_test ogre_resource_test +all: dummy_test ogre_client_test ogre_resource_test ogre_server_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -11,6 +11,9 @@ ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrap ogre_resource_test: ogre_resource_test.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) +ogre_server_test: ogre_server_test.cpp ../vfs.h ../imp_server/ogre_vfs.h ../imp_server/ogre_vfs.cpp + $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../imp_server/ogre_vfs.cpp + dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h $(GCC) $< -o $@ diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp index 92cb73eaa..a236c28f0 100644 --- a/vfs/tests/ogre_client_test.cpp +++ b/vfs/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ #include "dummy_vfs.cpp" -#include "ogre_archive.h" +#include "../imp_client/ogre_archive.h" #include using namespace Ogre; diff --git a/vfs/tests/ogre_resource_test.cpp b/vfs/tests/ogre_resource_test.cpp index 36f84c241..eadb1153f 100644 --- a/vfs/tests/ogre_resource_test.cpp +++ b/vfs/tests/ogre_resource_test.cpp @@ -30,32 +30,6 @@ void find(const std::string &fileName) cout << "Size: " << data->size() << endl; cout << "First line: " << data->getLine() << "\n"; - - - // Alternative - not used / fixed yet - - /* This won't work, since we don't have access to Ogre - internals. That's a shame. - - LocationList::iterator li, liend; - liend = grp->locationList.end(); - for (li = grp->locationList.begin(); li != liend; ++li) - { - Archive* arch = (*li)->archive; - - // The rest is client code - using an archive. We might make a - // shared implementation, or possibly convert the archives into - // a vfs list at load time (although that isn't very flexible.) - - // Do we perform these searches in each function? I guess we - // have to. - if (arch->exists(resourceName)) - { - DataStreamPtr ptr = arch->open(resourceName); - return ptr; - } - } - */ } int main() diff --git a/vfs/tests/ogre_server_test.cpp b/vfs/tests/ogre_server_test.cpp new file mode 100644 index 000000000..4193bda7f --- /dev/null +++ b/vfs/tests/ogre_server_test.cpp @@ -0,0 +1,63 @@ +#include "../imp_server/ogre_vfs.h" +#include + +#include + +using namespace std; +using namespace Mangle::VFS; +using namespace Mangle::Stream; + +Ogre::Root *root; + +void setupOgre() +{ + using namespace Ogre; + + // Disable logging + new LogManager; + Log *log = LogManager::getSingleton().createLog(""); + log->setDebugOutputEnabled(false); + + // Set up Root + root = new Root("","",""); + + // Add a zip file and the current directory + root->addResourceLocation("test.zip", "Zip", "General"); + root->addResourceLocation("./", "FileSystem", "General"); +} + +void find(VFS &vfs, const std::string &file) +{ + cout << "\nFile: " << file << endl; + + if(!vfs.isFile(file)) + { + cout << "File doesn't exist\n"; + return; + } + + InputStream *data = vfs.open(file); + + cout << "Size: " << data->size() << endl; + + char buf[13]; + buf[12] = 0; + data->read(buf, 12); + + cout << "First 12 bytes: " << buf << "\n"; +} + +int main() +{ + // Set up the engine + setupOgre(); + + // This is our entry point into the resource file system + OgreVFS vfs("General"); + + find(vfs, "Makefile"); // From the file system + find(vfs, "testfile.txt"); // From the zip + find(vfs, "blah_bleh"); // Doesn't exist + + return 0; +} diff --git a/vfs/tests/test.zip b/vfs/tests/test.zip new file mode 100644 index 0000000000000000000000000000000000000000..ec82f8bc6be46902264847237d30818665e89072 GIT binary patch literal 169 zcmWIWW@h1H0D;ZXv#htQzs|@DWP>mdgD68uYH>+gW=^VJNkvI$2qy#cq^G9dGk`d> zf`#D)^9$yT)SR4rh4TEOoD@Z_0B=Snab{emfy`uJUcwofH({Q*9Ik` literal 0 HcmV?d00001 From d763b9dbb62fefe0f0ab4c641f051762fb7b1ad9 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 13:35:34 +0100 Subject: [PATCH 013/111] Added PhysFS VFS server +tests --- stream/imp_server/phys_stream.h | 34 +++++++++++++++ vfs/imp_server/physfs_vfs.h | 71 ++++++++++++++++++++++++++++++++ vfs/tests/Makefile | 6 ++- vfs/tests/ogre_server_test.cpp | 31 ++------------ vfs/tests/physfs_server_test.cpp | 21 ++++++++++ vfs/tests/server_common.cpp | 33 +++++++++++++++ 6 files changed, 167 insertions(+), 29 deletions(-) create mode 100644 stream/imp_server/phys_stream.h create mode 100644 vfs/imp_server/physfs_vfs.h create mode 100644 vfs/tests/physfs_server_test.cpp create mode 100644 vfs/tests/server_common.cpp diff --git a/stream/imp_server/phys_stream.h b/stream/imp_server/phys_stream.h new file mode 100644 index 000000000..956619436 --- /dev/null +++ b/stream/imp_server/phys_stream.h @@ -0,0 +1,34 @@ +#ifndef MANGLE_STREAM_OGRESERVER_H +#define MANGLE_STREAM_OGRESERVER_H + +#include + +namespace Mangle { +namespace Stream { + +/// A Stream wrapping a PHYSFS_file stream from the PhysFS library. +class PhysFile : public InputStream +{ + PHYSFS_file *file; + + public: + PhysFile(PHYSFS_file *inp) : file(inp) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + ~PhysFile() { PHYSFS_close(file); } + + size_t read(void *buf, size_t count) + { return PHYSFS_read(file, buf, 1, count); } + + void seek(size_t pos) { PHYSFS_seek(file, pos); } + size_t tell() const { return PHYSFS_tell(file); } + size_t size() const { return PHYSFS_fileLength(file); } + bool eof() const { return PHYSFS_eof(file); } +}; + +}} // namespaces +#endif diff --git a/vfs/imp_server/physfs_vfs.h b/vfs/imp_server/physfs_vfs.h new file mode 100644 index 000000000..ace3d84c9 --- /dev/null +++ b/vfs/imp_server/physfs_vfs.h @@ -0,0 +1,71 @@ +#ifndef MANGLE_VFS_PHYSFS_SERVER_H +#define MANGLE_VFS_PHYSFS_SERVER_H + +#include "../vfs.h" +#include "../../stream/imp_server/phys_stream.h" + +#include +#include + +namespace Mangle { +namespace VFS { + +/** @brief An interface into the PhysFS virtual file system library + + You have to set up PhysFS on your own before using this class. + */ +class PhysVFS : public VFS +{ + public: + PhysVFS() + { + hasList = true; + hasFind = false; + isCaseSensitive = true; + } + + /// Open a new data stream. Deleting the object should be enough to + /// close it. + virtual Stream::InputStream *open(const std::string &name) + { return new Stream::PhysFile(PHYSFS_openRead(name.c_str())); } + + /// Check for the existence of a file + virtual bool isFile(const std::string &name) const + { return PHYSFS_exists(name.c_str()); } + + /// Checks for a directory + virtual bool isDir(const std::string &name) const + { return PHYSFS_isDirectory(name.c_str()); } + + /// This doesn't work + virtual FileInfo stat(const std::string &name) const + { assert(0); return FileInfo(); } + + virtual FileInfoList list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const + { + char **files = PHYSFS_enumerateFiles(dir.c_str()); + FileInfoList lst; + + // Add all teh files + int i = 0; + while(files[i] != NULL) + { + FileInfo fi; + fi.name = files[i]; + fi.isDir = false; + + lst.push_back(fi); + } + return lst; + } + + virtual FileInfoList find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const + { assert(0); } +}; + +}} // namespaces +#endif diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index 77e02f927..2cf722550 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -1,9 +1,10 @@ GCC=g++ -I../ -all: dummy_test ogre_client_test ogre_resource_test ogre_server_test +all: dummy_test ogre_client_test ogre_resource_test ogre_server_test physfs_server_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) +L_PHYSFS=-lphysfs ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrapper.h ../imp_client/ogre_archive.h ../imp_client/ogre_archive.cpp $(GCC) $< ../imp_client/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) @@ -14,6 +15,9 @@ ogre_resource_test: ogre_resource_test.cpp ogre_server_test: ogre_server_test.cpp ../vfs.h ../imp_server/ogre_vfs.h ../imp_server/ogre_vfs.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../imp_server/ogre_vfs.cpp +physfs_server_test: physfs_server_test.cpp ../vfs.h ../imp_server/physfs_vfs.h + $(GCC) $< -o $@ $(L_PHYSFS) + dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h $(GCC) $< -o $@ diff --git a/vfs/tests/ogre_server_test.cpp b/vfs/tests/ogre_server_test.cpp index 4193bda7f..3e0fcbef4 100644 --- a/vfs/tests/ogre_server_test.cpp +++ b/vfs/tests/ogre_server_test.cpp @@ -1,11 +1,8 @@ #include "../imp_server/ogre_vfs.h" -#include #include -using namespace std; -using namespace Mangle::VFS; -using namespace Mangle::Stream; +#include "server_common.cpp" Ogre::Root *root; @@ -26,27 +23,6 @@ void setupOgre() root->addResourceLocation("./", "FileSystem", "General"); } -void find(VFS &vfs, const std::string &file) -{ - cout << "\nFile: " << file << endl; - - if(!vfs.isFile(file)) - { - cout << "File doesn't exist\n"; - return; - } - - InputStream *data = vfs.open(file); - - cout << "Size: " << data->size() << endl; - - char buf[13]; - buf[12] = 0; - data->read(buf, 12); - - cout << "First 12 bytes: " << buf << "\n"; -} - int main() { // Set up the engine @@ -55,9 +31,8 @@ int main() // This is our entry point into the resource file system OgreVFS vfs("General"); - find(vfs, "Makefile"); // From the file system - find(vfs, "testfile.txt"); // From the zip - find(vfs, "blah_bleh"); // Doesn't exist + // Run the test + testAll(vfs); return 0; } diff --git a/vfs/tests/physfs_server_test.cpp b/vfs/tests/physfs_server_test.cpp new file mode 100644 index 000000000..282566dd1 --- /dev/null +++ b/vfs/tests/physfs_server_test.cpp @@ -0,0 +1,21 @@ +#include "../imp_server/physfs_vfs.h" + +#include "server_common.cpp" + +#include + +int main() +{ + // Set up the library and paths + PHYSFS_init("blah"); + PHYSFS_addToSearchPath("test.zip", 1); + PHYSFS_addToSearchPath("./", 1); + + // Create our interface + PhysVFS vfs; + + // Run the test + testAll(vfs); + + return 0; +} diff --git a/vfs/tests/server_common.cpp b/vfs/tests/server_common.cpp new file mode 100644 index 000000000..ddb85b0c3 --- /dev/null +++ b/vfs/tests/server_common.cpp @@ -0,0 +1,33 @@ +#include + +using namespace Mangle::VFS; +using namespace Mangle::Stream; +using namespace std; + +void find(VFS &vfs, const std::string &file) +{ + cout << "\nFile: " << file << endl; + + if(!vfs.isFile(file)) + { + cout << "File doesn't exist\n"; + return; + } + + InputStream *data = vfs.open(file); + + cout << "Size: " << data->size() << endl; + + char buf[13]; + buf[12] = 0; + data->read(buf, 12); + + cout << "First 12 bytes: " << buf << "\n"; +} + +void testAll(VFS &vfs) +{ + find(vfs, "Makefile"); // From the file system + find(vfs, "testfile.txt"); // From the zip + find(vfs, "blah_bleh"); // Doesn't exist +} From abee2689e362eeafeba8ebdcfa346e6fcff2ba38 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 14:49:43 +0100 Subject: [PATCH 014/111] Added stream capability to Audiere input --- sound/imp/input_audiere.cpp | 16 +++++++++-- sound/imp/input_audiere.h | 1 + sound/tests/Makefile | 2 +- sound/tests/common.cpp | 54 ++++++++++++++++++++++++++++++++++--- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/sound/imp/input_audiere.cpp b/sound/imp/input_audiere.cpp index 624b2050b..b7a38ffd1 100644 --- a/sound/imp/input_audiere.cpp +++ b/sound/imp/input_audiere.cpp @@ -1,6 +1,8 @@ #include "input_audiere.h" #include +#include "../../stream/imp_client/audiere_file.h" + // Exception handling class Audiere_Exception : public std::exception { @@ -25,14 +27,14 @@ using namespace Mangle::Sound; AudiereInput::AudiereInput() { - canLoadStream = false; + canLoadStream = true; } InputSource *AudiereInput::load(const std::string &file) { return new AudiereSource(file); } InputSource *AudiereInput::load(Stream::InputStream *input) -{ assert(0 && "not implemented yet"); } +{ return new AudiereSource(input); } // --- InputSource --- @@ -45,6 +47,16 @@ AudiereSource::AudiereSource(const std::string &file) buf = CreateSampleBuffer(sample); } +AudiereSource::AudiereSource(Stream::InputStream *input) +{ + SampleSourcePtr sample = OpenSampleSource + (new Stream::AudiereFile(input)); + if(!sample) + fail("Couldn't load stream"); + + buf = CreateSampleBuffer(sample); +} + InputStream *AudiereSource::getStream() { return new AudiereStream(buf->openStream()); diff --git a/sound/imp/input_audiere.h b/sound/imp/input_audiere.h index 344c07735..4bc42cbc6 100644 --- a/sound/imp/input_audiere.h +++ b/sound/imp/input_audiere.h @@ -28,6 +28,7 @@ class AudiereSource : public InputSource public: AudiereSource(const std::string &file); + AudiereSource(Stream::InputStream *input); InputStream *getStream(); void drop() { delete this; } }; diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 22da35432..5d38b4d1d 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -9,7 +9,7 @@ L_AUDIERE=-laudiere ffmpeg_openal_test: ffmpeg_openal_test.cpp ../imp/input_ffmpeg.cpp ../imp/output_openal.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) -openal_audiere_test: openal_audiere_test.cpp ../imp/input_audiere.cpp ../imp/output_openal.cpp +openal_audiere_test: openal_audiere_test.cpp ../imp/input_audiere.cpp ../imp/output_openal.cpp ../../stream/imp_client/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) audiere_test: audiere_test.cpp ../imp/audiere_imp.cpp diff --git a/sound/tests/common.cpp b/sound/tests/common.cpp index 78dbce5b2..28c470536 100644 --- a/sound/tests/common.cpp +++ b/sound/tests/common.cpp @@ -1,20 +1,67 @@ // This file is included directly into the test programs #include +#include #include using namespace std; -void play(const char* name, bool music=false) +class TestStream : public Mangle::Stream::InputStream { - cout << "Playing " << name << "\n"; + ifstream io; + +public: + + TestStream(const char* name) + { + io.open(name, ios::binary); + isSeekable = true; + hasPosition = true; + hasSize = false; + } + + size_t read(void* buf, size_t len) + { + io.read((char*)buf, len); + return io.gcount(); + } + + void seek(size_t pos) + { + io.seekg(pos); + } + + size_t tell() const + { return ((TestStream*)this)->io.tellg(); } + + size_t size() const + { return 0; } + + bool eof() const + { return io.eof(); } +}; + +void play(const char* name, bool music=false, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; Sound *snd = NULL; Instance *s = NULL; try { - snd = mg.load(name, music); + if(stream) + snd = mg.load(new TestStream(name), music); + else + snd = mg.load(name, music); + + s = snd->getInstance(false, false); s->play(); @@ -37,5 +84,6 @@ int main() { play("cow.wav"); play("owl.ogg", true); + play("cow.wav", false, true); return 0; } From eedf0c9e3b2083bcb9ad4e3cb0ab202ee4106eba Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 22:13:06 +0100 Subject: [PATCH 015/111] Added composite test ogre+audiere+openal --- sound/imp/input_filter.h | 2 +- tests/.gitignore | 1 + tests/Makefile | 14 +++++++ tests/ogrevfs_audiere_openal_test.cpp | 52 ++++++++++++++++++++++++++ tests/sound.zip | Bin 0 -> 18159 bytes 5 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/.gitignore create mode 100644 tests/Makefile create mode 100644 tests/ogrevfs_audiere_openal_test.cpp create mode 100644 tests/sound.zip diff --git a/sound/imp/input_filter.h b/sound/imp/input_filter.h index c3da8599d..4fcae320c 100644 --- a/sound/imp/input_filter.h +++ b/sound/imp/input_filter.h @@ -1,7 +1,7 @@ #ifndef MANGLE_INPUT_FILTER_H #define MANGLE_INPUT_FILTER_H -#include "sound.h" +#include "../sound.h" #include diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 000000000..814490404 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 000000000..6aeaa5eb5 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,14 @@ +GCC=g++ -I../ + +all: ogrevfs_audiere_openal_test + +I_OGRE=$(shell pkg-config --cflags OGRE) +L_OGRE=$(shell pkg-config --libs OGRE) +L_OPENAL=$(shell pkg-config --libs openal) +L_AUDIERE=-laudiere + +ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/imp_server/ogre_vfs.cpp ../sound/imp/input_audiere.cpp ../sound/imp/output_openal.cpp ../stream/imp_client/audiere_file.cpp + $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) + +clean: + rm *_test diff --git a/tests/ogrevfs_audiere_openal_test.cpp b/tests/ogrevfs_audiere_openal_test.cpp new file mode 100644 index 000000000..7bc6131da --- /dev/null +++ b/tests/ogrevfs_audiere_openal_test.cpp @@ -0,0 +1,52 @@ +/* + This example combines: + + - the OGRE VFS system (to read from zip) + - Audiere (for decoding sound data) + - OpenAL (for sound playback) + + */ + +#include "sound/imp/openal_audiere.h" +#include "vfs/imp_server/ogre_vfs.h" +#include +#include + +using namespace Ogre; +using namespace Mangle; +using namespace std; + +int main() +{ + // Disable Ogre logging + new LogManager; + Log *log = LogManager::getSingleton().createLog(""); + log->setDebugOutputEnabled(false); + + // Set up Root + Root *root = new Root("","",""); + + // Add zip file with a sound in it + root->addResourceLocation("sound.zip", "Zip", "General"); + + // Ogre file system + VFS::OgreVFS vfs; + + Sound::OpenAL_Audiere_Manager mg; + Sound::Sound *snd = mg.load(vfs.open("owl.ogg")); + + Sound::Instance *s = snd->getInstance(false, false); + cout << "Playing 'owl.ogg' from 'sound.zip'\n"; + s->play(); + + while(s->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + + if(s) s->drop(); + if(snd) snd->drop(); + + return 0; +} diff --git a/tests/sound.zip b/tests/sound.zip new file mode 100644 index 0000000000000000000000000000000000000000..fd32b35299859ba2d92e702db719367110d8b994 GIT binary patch literal 18159 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 literal 0 HcmV?d00001 From 69e8f9c9dbc13580115d89d34ebad115f5c53161 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 28 Dec 2009 14:09:17 +0100 Subject: [PATCH 016/111] Renamed several dirs, files and classes. NOT TESTED. - renamed imp_client/ to clients/ and ditto for servers/ - renamed imp/ to servers/ - renamed stream/input.h to stream/stream.h - renamed Stream::InputStream to Stream::Stream - updated various tests and makefiles - NOT TESTED YET --- sound/input.h | 4 ++-- sound/{imp => servers}/.gitignore | 0 sound/{imp => servers}/audiere_imp.cpp | 0 sound/{imp => servers}/audiere_imp.h | 2 +- sound/{imp => servers}/input_audiere.cpp | 6 +++--- sound/{imp => servers}/input_audiere.h | 4 ++-- sound/{imp => servers}/input_ffmpeg.cpp | 0 sound/{imp => servers}/input_ffmpeg.h | 2 +- sound/{imp => servers}/input_filter.h | 2 +- sound/{imp => servers}/openal_audiere.h | 0 sound/{imp => servers}/openal_ffmpeg.h | 0 sound/{imp => servers}/output_openal.cpp | 2 +- sound/{imp => servers}/output_openal.h | 2 +- sound/sound.h | 4 ++-- sound/tests/Makefile | 8 ++++---- sound/tests/common.cpp | 2 +- stream/{imp_client => clients}/audiere_file.cpp | 0 stream/{imp_client => clients}/audiere_file.h | 6 +++--- stream/{imp_client => clients}/iwrapper.h | 12 ++++++------ stream/{imp_client => clients}/ogre_datastream.h | 10 +++++----- stream/{imp_server => servers}/ogre_datastream.h | 2 +- stream/{imp_server => servers}/phys_stream.h | 2 +- stream/{input.h => stream.h} | 4 ++-- stream/tests/Makefile | 10 +++++----- stream/tests/audiere_client_test.cpp | 4 ++-- stream/tests/dummy_input.cpp | 4 ++-- stream/tests/dummy_test.cpp | 2 +- stream/tests/ogre_client_test.cpp | 2 +- tests/Makefile | 2 +- tests/ogrevfs_audiere_openal_test.cpp | 4 ++-- vfs/{imp_client => clients}/ogre_archive.cpp | 2 +- vfs/{imp_client => clients}/ogre_archive.h | 0 vfs/{imp_client => clients}/wrapper.h | 0 vfs/{imp_server => servers}/ogre_vfs.cpp | 4 ++-- vfs/{imp_server => servers}/ogre_vfs.h | 2 +- vfs/{imp_server => servers}/physfs_vfs.h | 4 ++-- vfs/tests/Makefile | 10 +++++----- vfs/tests/dummy_test.cpp | 2 +- vfs/tests/dummy_vfs.cpp | 2 +- vfs/tests/ogre_client_test.cpp | 2 +- vfs/tests/ogre_server_test.cpp | 2 +- vfs/tests/physfs_server_test.cpp | 2 +- vfs/tests/server_common.cpp | 2 +- vfs/vfs.h | 4 ++-- 44 files changed, 70 insertions(+), 70 deletions(-) rename sound/{imp => servers}/.gitignore (100%) rename sound/{imp => servers}/audiere_imp.cpp (100%) rename sound/{imp => servers}/audiere_imp.h (95%) rename sound/{imp => servers}/input_audiere.cpp (94%) rename sound/{imp => servers}/input_audiere.h (92%) rename sound/{imp => servers}/input_ffmpeg.cpp (100%) rename sound/{imp => servers}/input_ffmpeg.h (95%) rename sound/{imp => servers}/input_filter.h (96%) rename sound/{imp => servers}/openal_audiere.h (100%) rename sound/{imp => servers}/openal_ffmpeg.h (100%) rename sound/{imp => servers}/output_openal.cpp (99%) rename sound/{imp => servers}/output_openal.h (97%) rename stream/{imp_client => clients}/audiere_file.cpp (100%) rename stream/{imp_client => clients}/audiere_file.h (88%) rename stream/{imp_client => clients}/iwrapper.h (58%) rename stream/{imp_client => clients}/ogre_datastream.h (78%) rename stream/{imp_server => servers}/ogre_datastream.h (95%) rename stream/{imp_server => servers}/phys_stream.h (95%) rename stream/{input.h => stream.h} (96%) rename vfs/{imp_client => clients}/ogre_archive.cpp (97%) rename vfs/{imp_client => clients}/ogre_archive.h (100%) rename vfs/{imp_client => clients}/wrapper.h (100%) rename vfs/{imp_server => servers}/ogre_vfs.cpp (91%) rename vfs/{imp_server => servers}/ogre_vfs.h (97%) rename vfs/{imp_server => servers}/physfs_vfs.h (93%) diff --git a/sound/input.h b/sound/input.h index d27de97a6..f61a029ff 100644 --- a/sound/input.h +++ b/sound/input.h @@ -4,7 +4,7 @@ #include #include -#include "../stream/input.h" +#include "../stream/stream.h" namespace Mangle { namespace Sound { @@ -73,7 +73,7 @@ class InputManager virtual InputSource *load(const std::string &file) = 0; /// Load a sound input source from stream (if canLoadStream is true) - virtual InputSource *load(Stream::InputStream *input) = 0; + virtual InputSource *load(Stream::Stream *input) = 0; /// Virtual destructor virtual ~InputManager() {} diff --git a/sound/imp/.gitignore b/sound/servers/.gitignore similarity index 100% rename from sound/imp/.gitignore rename to sound/servers/.gitignore diff --git a/sound/imp/audiere_imp.cpp b/sound/servers/audiere_imp.cpp similarity index 100% rename from sound/imp/audiere_imp.cpp rename to sound/servers/audiere_imp.cpp diff --git a/sound/imp/audiere_imp.h b/sound/servers/audiere_imp.h similarity index 95% rename from sound/imp/audiere_imp.h rename to sound/servers/audiere_imp.h index 480611ce3..5ebb812bd 100644 --- a/sound/imp/audiere_imp.h +++ b/sound/servers/audiere_imp.h @@ -20,7 +20,7 @@ class AudiereManager : public Manager virtual Sound *load(const std::string &file, bool stream=false); /// not implemented yet - virtual Sound *load(Stream::InputStream *input, bool stream=false) + virtual Sound *load(Stream::Stream *input, bool stream=false) { assert(0); } /// disabled diff --git a/sound/imp/input_audiere.cpp b/sound/servers/input_audiere.cpp similarity index 94% rename from sound/imp/input_audiere.cpp rename to sound/servers/input_audiere.cpp index b7a38ffd1..c48f45013 100644 --- a/sound/imp/input_audiere.cpp +++ b/sound/servers/input_audiere.cpp @@ -1,7 +1,7 @@ #include "input_audiere.h" #include -#include "../../stream/imp_client/audiere_file.h" +#include "../../stream/clients/audiere_file.h" // Exception handling class Audiere_Exception : public std::exception @@ -33,7 +33,7 @@ AudiereInput::AudiereInput() InputSource *AudiereInput::load(const std::string &file) { return new AudiereSource(file); } -InputSource *AudiereInput::load(Stream::InputStream *input) +InputSource *AudiereInput::load(Stream::Stream *input) { return new AudiereSource(input); } // --- InputSource --- @@ -47,7 +47,7 @@ AudiereSource::AudiereSource(const std::string &file) buf = CreateSampleBuffer(sample); } -AudiereSource::AudiereSource(Stream::InputStream *input) +AudiereSource::AudiereSource(Stream::Stream *input) { SampleSourcePtr sample = OpenSampleSource (new Stream::AudiereFile(input)); diff --git a/sound/imp/input_audiere.h b/sound/servers/input_audiere.h similarity index 92% rename from sound/imp/input_audiere.h rename to sound/servers/input_audiere.h index 4bc42cbc6..e753b0174 100644 --- a/sound/imp/input_audiere.h +++ b/sound/servers/input_audiere.h @@ -18,7 +18,7 @@ class AudiereInput : public InputManager InputSource *load(const std::string &file); /// Load a source from a stream - virtual InputSource *load(Stream::InputStream *input); + virtual InputSource *load(Stream::Stream *input); }; /// Audiere InputSource implementation @@ -28,7 +28,7 @@ class AudiereSource : public InputSource public: AudiereSource(const std::string &file); - AudiereSource(Stream::InputStream *input); + AudiereSource(Stream::Stream *input); InputStream *getStream(); void drop() { delete this; } }; diff --git a/sound/imp/input_ffmpeg.cpp b/sound/servers/input_ffmpeg.cpp similarity index 100% rename from sound/imp/input_ffmpeg.cpp rename to sound/servers/input_ffmpeg.cpp diff --git a/sound/imp/input_ffmpeg.h b/sound/servers/input_ffmpeg.h similarity index 95% rename from sound/imp/input_ffmpeg.h rename to sound/servers/input_ffmpeg.h index ae25732b5..b744a7585 100644 --- a/sound/imp/input_ffmpeg.h +++ b/sound/servers/input_ffmpeg.h @@ -37,7 +37,7 @@ class FFM_InputManager : public InputManager virtual InputSource *load(const std::string &file); /// not supported - virtual InputSource *load(Stream::InputStream *input) { assert(0); } + virtual InputSource *load(Stream::Stream *input) { assert(0); } }; /// FFMpeg implementation of InputSource diff --git a/sound/imp/input_filter.h b/sound/servers/input_filter.h similarity index 96% rename from sound/imp/input_filter.h rename to sound/servers/input_filter.h index 4fcae320c..455a60e14 100644 --- a/sound/imp/input_filter.h +++ b/sound/servers/input_filter.h @@ -63,7 +63,7 @@ class InputFilter : public Manager virtual Sound *load(const std::string &file, bool stream=false) { return load(inp->load(file), stream); } - virtual Sound *load(Stream::InputStream *input, bool stream=false) + virtual Sound *load(Stream::Stream *input, bool stream=false) { return load(inp->load(input), stream); } virtual Sound *load(InputSource *input, bool stream=false) diff --git a/sound/imp/openal_audiere.h b/sound/servers/openal_audiere.h similarity index 100% rename from sound/imp/openal_audiere.h rename to sound/servers/openal_audiere.h diff --git a/sound/imp/openal_ffmpeg.h b/sound/servers/openal_ffmpeg.h similarity index 100% rename from sound/imp/openal_ffmpeg.h rename to sound/servers/openal_ffmpeg.h diff --git a/sound/imp/output_openal.cpp b/sound/servers/output_openal.cpp similarity index 99% rename from sound/imp/output_openal.cpp rename to sound/servers/output_openal.cpp index e36981836..06a8edca8 100644 --- a/sound/imp/output_openal.cpp +++ b/sound/servers/output_openal.cpp @@ -98,7 +98,7 @@ OpenAL_Manager::~OpenAL_Manager() Sound *OpenAL_Manager::load(const std::string &file, bool stream) { assert(0 && "OpenAL cannot decode files"); } -Sound *OpenAL_Manager::load(Stream::InputStream*,bool) +Sound *OpenAL_Manager::load(Stream::Stream*,bool) { assert(0 && "OpenAL cannot decode streams"); } Sound *OpenAL_Manager::load(InputSource *source, bool stream) diff --git a/sound/imp/output_openal.h b/sound/servers/output_openal.h similarity index 97% rename from sound/imp/output_openal.h rename to sound/servers/output_openal.h index bf92197df..53226c32f 100644 --- a/sound/imp/output_openal.h +++ b/sound/servers/output_openal.h @@ -26,7 +26,7 @@ public: void remove_stream(LST::iterator); virtual Sound *load(const std::string &file, bool stream=false); - virtual Sound *load(Stream::InputStream *input, bool stream=false); + virtual Sound *load(Stream::Stream *input, bool stream=false); virtual Sound *load(InputSource* input, bool stream=false); virtual void update(); virtual void setListenerPos(float x, float y, float z, diff --git a/sound/sound.h b/sound/sound.h index 90407141e..0a51e9f93 100644 --- a/sound/sound.h +++ b/sound/sound.h @@ -4,7 +4,7 @@ #include #include "input.h" -#include "../stream/input.h" +#include "../stream/stream.h" namespace Mangle { namespace Sound { @@ -145,7 +145,7 @@ class Manager @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(Stream::InputStream *input, bool stream=false) = 0; + virtual Sound *load(Stream::Stream *input, bool stream=false) = 0; /** @brief Load a sound directly from file. Only valid if canLoadFile diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 5d38b4d1d..8a9c74812 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ -I../imp/ +GCC=g++ -I../ all: audiere_test ffmpeg_openal_test openal_audiere_test @@ -6,13 +6,13 @@ L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -ffmpeg_openal_test: ffmpeg_openal_test.cpp ../imp/input_ffmpeg.cpp ../imp/output_openal.cpp +ffmpeg_openal_test: ffmpeg_openal_test.cpp ../servers/input_ffmpeg.cpp ../servers/output_openal.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) -openal_audiere_test: openal_audiere_test.cpp ../imp/input_audiere.cpp ../imp/output_openal.cpp ../../stream/imp_client/audiere_file.cpp +openal_audiere_test: openal_audiere_test.cpp ../servers/input_audiere.cpp ../servers/output_openal.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -audiere_test: audiere_test.cpp ../imp/audiere_imp.cpp +audiere_test: audiere_test.cpp ../servers/audiere_imp.cpp $(GCC) $^ -o $@ $(L_AUDIERE) clean: diff --git a/sound/tests/common.cpp b/sound/tests/common.cpp index 28c470536..818d1645f 100644 --- a/sound/tests/common.cpp +++ b/sound/tests/common.cpp @@ -6,7 +6,7 @@ using namespace std; -class TestStream : public Mangle::Stream::InputStream +class TestStream : public Mangle::Stream::Stream { ifstream io; diff --git a/stream/imp_client/audiere_file.cpp b/stream/clients/audiere_file.cpp similarity index 100% rename from stream/imp_client/audiere_file.cpp rename to stream/clients/audiere_file.cpp diff --git a/stream/imp_client/audiere_file.h b/stream/clients/audiere_file.h similarity index 88% rename from stream/imp_client/audiere_file.h rename to stream/clients/audiere_file.h index a1910d274..87833e6b3 100644 --- a/stream/imp_client/audiere_file.h +++ b/stream/clients/audiere_file.h @@ -13,11 +13,11 @@ namespace Stream { This lets Audiere read sound files from any generic archive or file manager that supports Mangle streams. */ -class AudiereFile : public audiere::RefImplementation, _IWrapper +class AudiereFile : public audiere::RefImplementation, _SWrapper { public: - AudiereFile(InputStream *inp, bool autoDel=false) - : _IWrapper(inp, autoDel) {} + AudiereFile(Stream *inp, bool autoDel=false) + : _SWrapper(inp, autoDel) {} /// Read 'count' bytes, return bytes successfully read int read(void *buf, int count) diff --git a/stream/imp_client/iwrapper.h b/stream/clients/iwrapper.h similarity index 58% rename from stream/imp_client/iwrapper.h rename to stream/clients/iwrapper.h index ba2d1fb1f..cc12d5b7e 100644 --- a/stream/imp_client/iwrapper.h +++ b/stream/clients/iwrapper.h @@ -1,29 +1,29 @@ #ifndef MANGLE_STREAM_IWRAPPER_H #define MANGLE_STREAM_IWRAPPER_H -#include "../input.h" +#include "../stream.h" #include namespace Mangle { namespace Stream { -/** A generic wrapper class for a Stream::Input object. +/** A generic wrapper class for a Stream::Stream object. This is used by other implementations. */ -class _IWrapper +class _SWrapper { private: bool autoDel; protected: - InputStream *inp; + Stream *inp; public: - _IWrapper(InputStream *_inp, bool _autoDel = false) + _SWrapper(Stream *_inp, bool _autoDel = false) : inp(_inp), autoDel(_autoDel) { assert(inp != NULL); } - virtual ~_IWrapper() { if(autoDel) delete inp; } + virtual ~_SWrapper() { if(autoDel) delete inp; } }; }} // namespaces diff --git a/stream/imp_client/ogre_datastream.h b/stream/clients/ogre_datastream.h similarity index 78% rename from stream/imp_client/ogre_datastream.h rename to stream/clients/ogre_datastream.h index 63ca66e20..6d0979930 100644 --- a/stream/imp_client/ogre_datastream.h +++ b/stream/clients/ogre_datastream.h @@ -14,7 +14,7 @@ namespace Stream { to make your own modifications if you're working with newer (or older) versions. */ -class MangleDataStream : public Ogre::DataStream, _IWrapper +class MangleDataStream : public Ogre::DataStream, _SWrapper { void init() { @@ -25,12 +25,12 @@ class MangleDataStream : public Ogre::DataStream, _IWrapper public: /// Constructor without name - MangleDataStream(InputStream *inp, bool autoDel=false) - : _IWrapper(inp, autoDel) { init(); } + MangleDataStream(Stream *inp, bool autoDel=false) + : _SWrapper(inp, autoDel) { init(); } /// Constructor for a named data stream - MangleDataStream(const Ogre::String &name, InputStream *inp, bool autoDel=false) - : _IWrapper(inp, autoDel), Ogre::DataStream(name) { init(); } + MangleDataStream(const Ogre::String &name, Stream *inp, bool autoDel=false) + : _SWrapper(inp, autoDel), Ogre::DataStream(name) { init(); } // Only implement the DataStream functions we have to implement diff --git a/stream/imp_server/ogre_datastream.h b/stream/servers/ogre_datastream.h similarity index 95% rename from stream/imp_server/ogre_datastream.h rename to stream/servers/ogre_datastream.h index ef922fa7f..184fa1668 100644 --- a/stream/imp_server/ogre_datastream.h +++ b/stream/servers/ogre_datastream.h @@ -12,7 +12,7 @@ namespace Stream { to make your own modifications if you're working with newer (or older) versions. */ -class OgreStream : public InputStream +class OgreStream : public Stream { Ogre::DataStreamPtr inp; diff --git a/stream/imp_server/phys_stream.h b/stream/servers/phys_stream.h similarity index 95% rename from stream/imp_server/phys_stream.h rename to stream/servers/phys_stream.h index 956619436..3660391fd 100644 --- a/stream/imp_server/phys_stream.h +++ b/stream/servers/phys_stream.h @@ -7,7 +7,7 @@ namespace Mangle { namespace Stream { /// A Stream wrapping a PHYSFS_file stream from the PhysFS library. -class PhysFile : public InputStream +class PhysFile : public Stream { PHYSFS_file *file; diff --git a/stream/input.h b/stream/stream.h similarity index 96% rename from stream/input.h rename to stream/stream.h index 0a178d8fc..7116b8e2a 100644 --- a/stream/input.h +++ b/stream/stream.h @@ -7,7 +7,7 @@ namespace Mangle { namespace Stream { /// An abstract interface for a stream data. -class InputStream +class Stream { public: // Feature options. These should be set in the constructor. @@ -22,7 +22,7 @@ class InputStream bool hasSize; /// Virtual destructor - virtual ~InputStream() {} + virtual ~Stream() {} /** Read a given number of bytes from the stream. Returns the actual number read. If the return value is less than count, then the diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 3d2a730b8..84ec228cf 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ -I../imp_client/ +GCC=g++ -I../ all: ogre_client_test dummy_test audiere_client_test @@ -6,13 +6,13 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_AUDIERE=-laudiere -ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/ogre_datastream.h +ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../stream.h ../clients/iwrapper.h ../clients/ogre_datastream.h $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -audiere_client_test: audiere_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/audiere_file.h ../imp_client/audiere_file.cpp - $(GCC) $< -o $@ ../imp_client/audiere_file.cpp $(L_AUDIERE) +audiere_client_test: audiere_client_test.cpp dummy_input.cpp ../stream.h ../clients/iwrapper.h ../clients/audiere_file.h ../clients/audiere_file.cpp + $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -dummy_test: dummy_test.cpp dummy_input.cpp ../input.h +dummy_test: dummy_test.cpp dummy_input.cpp ../stream.h $(GCC) $< -o $@ clean: diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp index 7a5b5afc2..f64de0649 100644 --- a/stream/tests/audiere_client_test.cpp +++ b/stream/tests/audiere_client_test.cpp @@ -1,5 +1,5 @@ #include "dummy_input.cpp" -#include "../imp_client/audiere_file.h" +#include "../clients/audiere_file.h" #include #include @@ -10,7 +10,7 @@ int main() { char str[12]; memset(str, 0, 12); - InputStream *inp = new DummyInput(); + Stream *inp = new DummyInput(); FilePtr p(new AudiereFile(inp, true)); cout << "pos=" << p->tell() << endl; p->read(str, 2); diff --git a/stream/tests/dummy_input.cpp b/stream/tests/dummy_input.cpp index 0bfe5ad52..c93f42160 100644 --- a/stream/tests/dummy_input.cpp +++ b/stream/tests/dummy_input.cpp @@ -1,5 +1,5 @@ // This file is shared between several test programs -#include "../input.h" +#include "../stream.h" #include #include @@ -8,7 +8,7 @@ using namespace Mangle::Stream; // A simple dummy stream const char _data[12] = "hello world"; -class DummyInput : public InputStream +class DummyInput : public Stream { private: int pos; diff --git a/stream/tests/dummy_test.cpp b/stream/tests/dummy_test.cpp index 64dd8f19b..cd8f64e3c 100644 --- a/stream/tests/dummy_test.cpp +++ b/stream/tests/dummy_test.cpp @@ -7,7 +7,7 @@ using namespace std; int main() { - InputStream *inp = new DummyInput(); + Stream *inp = new DummyInput(); cout << "Size: " << inp->size() << endl; cout << "Pos: " << inp->tell() << "\nSeeking...\n"; diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp index b6a46ad3b..a2bae1c8e 100644 --- a/stream/tests/ogre_client_test.cpp +++ b/stream/tests/ogre_client_test.cpp @@ -7,7 +7,7 @@ using namespace std; int main() { - InputStream *inp = new DummyInput(); + Stream *inp = new DummyInput(); DataStreamPtr p(new MangleDataStream("hello", inp, true)); cout << "Name: " << p->getName() << endl; cout << "As string: " << p->getAsString() << endl; diff --git a/tests/Makefile b/tests/Makefile index 6aeaa5eb5..ed680f3db 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,7 @@ L_OGRE=$(shell pkg-config --libs OGRE) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/imp_server/ogre_vfs.cpp ../sound/imp/input_audiere.cpp ../sound/imp/output_openal.cpp ../stream/imp_client/audiere_file.cpp +ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/servers/input_audiere.cpp ../sound/servers/output_openal.cpp ../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) clean: diff --git a/tests/ogrevfs_audiere_openal_test.cpp b/tests/ogrevfs_audiere_openal_test.cpp index 7bc6131da..810fd635e 100644 --- a/tests/ogrevfs_audiere_openal_test.cpp +++ b/tests/ogrevfs_audiere_openal_test.cpp @@ -7,8 +7,8 @@ */ -#include "sound/imp/openal_audiere.h" -#include "vfs/imp_server/ogre_vfs.h" +#include "sound/servers/openal_audiere.h" +#include "vfs/servers/ogre_vfs.h" #include #include diff --git a/vfs/imp_client/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp similarity index 97% rename from vfs/imp_client/ogre_archive.cpp rename to vfs/clients/ogre_archive.cpp index d7adaa07d..936eda9b6 100644 --- a/vfs/imp_client/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -1,6 +1,6 @@ #include "ogre_archive.h" -#include "../../stream/imp_client/ogre_datastream.h" +#include "../../stream/clients/ogre_datastream.h" using namespace Mangle::VFS; using namespace Mangle::Stream; diff --git a/vfs/imp_client/ogre_archive.h b/vfs/clients/ogre_archive.h similarity index 100% rename from vfs/imp_client/ogre_archive.h rename to vfs/clients/ogre_archive.h diff --git a/vfs/imp_client/wrapper.h b/vfs/clients/wrapper.h similarity index 100% rename from vfs/imp_client/wrapper.h rename to vfs/clients/wrapper.h diff --git a/vfs/imp_server/ogre_vfs.cpp b/vfs/servers/ogre_vfs.cpp similarity index 91% rename from vfs/imp_server/ogre_vfs.cpp rename to vfs/servers/ogre_vfs.cpp index 9ca1c4b8c..af1f7c963 100644 --- a/vfs/imp_server/ogre_vfs.cpp +++ b/vfs/servers/ogre_vfs.cpp @@ -1,5 +1,5 @@ #include "ogre_vfs.h" -#include "../../stream/imp_server/ogre_datastream.h" +#include "../../stream/servers/ogre_datastream.h" using namespace Mangle::VFS; @@ -18,7 +18,7 @@ OgreVFS::OgreVFS(const std::string &_group) group = gm->getWorldResourceGroupName(); } -Mangle::Stream::InputStream *OgreVFS::open(const std::string &name) +Mangle::Stream::Stream *OgreVFS::open(const std::string &name) { Ogre::DataStreamPtr data = gm->openResource(name, group); return new Stream::OgreStream(data); diff --git a/vfs/imp_server/ogre_vfs.h b/vfs/servers/ogre_vfs.h similarity index 97% rename from vfs/imp_server/ogre_vfs.h rename to vfs/servers/ogre_vfs.h index 9a01c1786..8fe2cca1c 100644 --- a/vfs/imp_server/ogre_vfs.h +++ b/vfs/servers/ogre_vfs.h @@ -36,7 +36,7 @@ class OgreVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::InputStream *open(const std::string &name); + virtual Stream::Stream *open(const std::string &name); /// Check for the existence of a file virtual bool isFile(const std::string &name) const diff --git a/vfs/imp_server/physfs_vfs.h b/vfs/servers/physfs_vfs.h similarity index 93% rename from vfs/imp_server/physfs_vfs.h rename to vfs/servers/physfs_vfs.h index ace3d84c9..c25a0035c 100644 --- a/vfs/imp_server/physfs_vfs.h +++ b/vfs/servers/physfs_vfs.h @@ -2,7 +2,7 @@ #define MANGLE_VFS_PHYSFS_SERVER_H #include "../vfs.h" -#include "../../stream/imp_server/phys_stream.h" +#include "../../stream/servers/phys_stream.h" #include #include @@ -26,7 +26,7 @@ class PhysVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::InputStream *open(const std::string &name) + virtual Stream::Stream *open(const std::string &name) { return new Stream::PhysFile(PHYSFS_openRead(name.c_str())); } /// Check for the existence of a file diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index 2cf722550..4595c82ea 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -6,16 +6,16 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_PHYSFS=-lphysfs -ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrapper.h ../imp_client/ogre_archive.h ../imp_client/ogre_archive.cpp - $(GCC) $< ../imp_client/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) +ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../clients/wrapper.h ../clients/ogre_archive.h ../clients/ogre_archive.cpp + $(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) ogre_resource_test: ogre_resource_test.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -ogre_server_test: ogre_server_test.cpp ../vfs.h ../imp_server/ogre_vfs.h ../imp_server/ogre_vfs.cpp - $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../imp_server/ogre_vfs.cpp +ogre_server_test: ogre_server_test.cpp ../vfs.h ../servers/ogre_vfs.h ../servers/ogre_vfs.cpp + $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../servers/ogre_vfs.cpp -physfs_server_test: physfs_server_test.cpp ../vfs.h ../imp_server/physfs_vfs.h +physfs_server_test: physfs_server_test.cpp ../vfs.h ../servers/physfs_vfs.h $(GCC) $< -o $@ $(L_PHYSFS) dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h diff --git a/vfs/tests/dummy_test.cpp b/vfs/tests/dummy_test.cpp index e5659d62f..b65f38335 100644 --- a/vfs/tests/dummy_test.cpp +++ b/vfs/tests/dummy_test.cpp @@ -33,7 +33,7 @@ int main() cout << endl; print(vfs.stat("dir")); - InputStream *inp = vfs.open("file1"); + Stream *inp = vfs.open("file1"); cout << "filesize: " << inp->size() << endl; return 0; diff --git a/vfs/tests/dummy_vfs.cpp b/vfs/tests/dummy_vfs.cpp index d8b259f28..e7c77a753 100644 --- a/vfs/tests/dummy_vfs.cpp +++ b/vfs/tests/dummy_vfs.cpp @@ -17,7 +17,7 @@ public: } // We only support opening 'file1' at the moment. - Mangle::Stream::InputStream *open(const std::string &name) + Mangle::Stream::Stream *open(const std::string &name) { assert(name == "file1"); return new DummyInput(); diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp index a236c28f0..d38add4da 100644 --- a/vfs/tests/ogre_client_test.cpp +++ b/vfs/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ #include "dummy_vfs.cpp" -#include "../imp_client/ogre_archive.h" +#include "../clients/ogre_archive.h" #include using namespace Ogre; diff --git a/vfs/tests/ogre_server_test.cpp b/vfs/tests/ogre_server_test.cpp index 3e0fcbef4..cb4624894 100644 --- a/vfs/tests/ogre_server_test.cpp +++ b/vfs/tests/ogre_server_test.cpp @@ -1,4 +1,4 @@ -#include "../imp_server/ogre_vfs.h" +#include "../servers/ogre_vfs.h" #include diff --git a/vfs/tests/physfs_server_test.cpp b/vfs/tests/physfs_server_test.cpp index 282566dd1..ae42083fd 100644 --- a/vfs/tests/physfs_server_test.cpp +++ b/vfs/tests/physfs_server_test.cpp @@ -1,4 +1,4 @@ -#include "../imp_server/physfs_vfs.h" +#include "../servers/physfs_vfs.h" #include "server_common.cpp" diff --git a/vfs/tests/server_common.cpp b/vfs/tests/server_common.cpp index ddb85b0c3..ff16ac7f2 100644 --- a/vfs/tests/server_common.cpp +++ b/vfs/tests/server_common.cpp @@ -14,7 +14,7 @@ void find(VFS &vfs, const std::string &file) return; } - InputStream *data = vfs.open(file); + Stream *data = vfs.open(file); cout << "Size: " << data->size() << endl; diff --git a/vfs/vfs.h b/vfs/vfs.h index a9ffb6b62..4054c6069 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.h @@ -1,7 +1,7 @@ #ifndef MANGLE_VFS_H #define MANGLE_VFS_H -#include "../stream/input.h" +#include "../stream/stream.h" #include #include @@ -51,7 +51,7 @@ class VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::InputStream *open(const std::string &name) = 0; + virtual Stream::Stream *open(const std::string &name) = 0; /// Check for the existence of a file virtual bool isFile(const std::string &name) const = 0; From cb638cd44e71d0601734eac935484273f508b1a1 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 28 Dec 2009 17:15:52 +0100 Subject: [PATCH 017/111] Started reworking the sound system. Added sources/ - WIP --- sound/source.h | 63 ++++++++ sound/sources/audiere_source.cpp | 139 ++++++++++++++++++ sound/sources/audiere_source.h | 51 +++++++ sound/sources/ffmpeg_source.cpp | 238 +++++++++++++++++++++++++++++++ sound/sources/ffmpeg_source.h | 47 ++++++ sound/sources/loadertemplate.h | 26 ++++ 6 files changed, 564 insertions(+) create mode 100644 sound/source.h create mode 100644 sound/sources/audiere_source.cpp create mode 100644 sound/sources/audiere_source.h create mode 100644 sound/sources/ffmpeg_source.cpp create mode 100644 sound/sources/ffmpeg_source.h create mode 100644 sound/sources/loadertemplate.h diff --git a/sound/source.h b/sound/source.h new file mode 100644 index 000000000..7361fb118 --- /dev/null +++ b/sound/source.h @@ -0,0 +1,63 @@ +#ifndef MANGLE_SOUND_SOURCE_H +#define MANGLE_SOUND_SOURCE_H + +#include +#include +#include + +#include "../stream/stream.h" + +namespace Mangle { +namespace Sound { + +/// A stream containing raw sound data and information about the format +class SampleSource : public Stream::Stream +{ + protected: + bool isEof; + + public: + SampleSource() + { + // These are usually not needed for sound data + isSeekable = false; + hasPosition = false; + hasSize = false; + + isEof = false; + } + + /// Get the sample rate, number of channels, and bits per + /// sample. NULL parameters are ignored. + virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) const = 0; + + bool eof() const { return isEof; } + + // Disabled functions + void seek(size_t pos) const { assert(0); } + size_t tell() const { assert(0); } + size_t size() const { assert(0); } +}; + +/// A factory interface for loading SampleSources from file or stream +class SampleSourceLoader +{ + public: + /// If true, the stream version of load() works + bool canLoadStream; + + /// If true, the file version of load() works + bool canLoadFile; + + /// Load a sound input source from file (if canLoadFile is true) + virtual SampleSource *load(const std::string &file) = 0; + + /// Load a sound input source from stream (if canLoadStream is true) + virtual SampleSource *load(Stream::Stream *input) = 0; + + /// Virtual destructor + virtual ~SampleSourceLoader() {} +}; + +}} // namespaces +#endif diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp new file mode 100644 index 000000000..6ebc4f881 --- /dev/null +++ b/sound/sources/audiere_source.cpp @@ -0,0 +1,139 @@ +#include "audiere_source.h" + +#include "../../stream/clients/audiere_file.h" + +// Exception handling +class Audiere_Exception : public std::exception +{ + std::string msg; + + public: + + Audiere_Exception(const std::string &m) : msg(m) {} + ~Audiere_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw Audiere_Exception("Audiere exception: " + msg); +} + +using namespace audiere; +using namespace Mangle::Sound; + +// --- SampleSource --- + +void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + SampleFormat fmt; + sample->getFormat(*channels, *rate, fmt); + if(fmt == SF_U8) + *bits = 8; + else if(fmt == SF_S16) + *bits = 16; + else assert(0); +} + +/* + Get data. Since Audiere operates with frames, not bytes, there's a + little conversion magic going on here. We need to make sure we're + reading a whole number of frames - if not, we need to store the + remainding part of the last frame and remember it for the next read + operation. + */ +size_t AudiereSource::read(void *_data, size_t length) +{ + if(isEof) return 0; + + char *data = (char*)_data; + + // Move the remains from the last operation first + if(pullSize) + { + // pullSize is how much was stored the last time, so skip that. + memcpy(data, pullOver+pullSize, PSIZE-pullSize); + length -= pullSize; + data += pullSize; + } + + // Determine the overshoot up front + pullSize = length % frameSize; + + // Number of whole frames + int frames = length / frameSize; + + // Read the data + int res = sample->read(frames, data); + + if(res < frames) + isEof = true; + + // Are we missing data? If we're at the end of the stream, then this + // doesn't apply. + if(!isEof && pullSize) + { + // Read one more sample + if(sample->read(1, pullOver) != 0) + { + // Then, move as much of it as we can fit into the output + // data + memcpy(data+length-pullSize, pullOver, pullSize); + } + else + // Failed reading, we're out of data + isEof = true; + } + + // If we're at the end of the stream, then no data remains to be + // pulled over + if(isEof) + pullSize = 0; + + // Return the total number of bytes stored + return frameSize*res + pullSize; +} + +// --- Constructors --- + +AudiereSource::AudiereSource(const std::string &file) +{ + sample = OpenSampleSource(file.c_str()); + + if(!sample) + fail("Couldn't load file " + file); + + getFormat(); +} + +AudiereSource::AudiereSource(Stream::Stream *input) +{ + // Use our Stream::AudiereFile implementation to convert a Mangle + // 'Stream' to an Audiere 'File' + sample = OpenSampleSource(new Stream::AudiereFile(input)); + if(!sample) + fail("Couldn't load stream"); + + getFormat(); +} + +AudiereSource::AudiereSource(audiere::SampleSourcePtr src) + : sample(src) +{ assert(sample); getFormat(); } + +// Common function called from all constructors +AudiereSource::getFormat() +{ + assert(sample); + + SampleFormat fmt; + int channels, rate; + sample->getFormat(channels, rate, fmt); + + // Calculate the size of one frame + frameSize = GetSampleSize(fmt) * channels; + + // Make sure that our pullover hack will work. Increase this size if + // this doesn't work in all cases. + assert(frameSize <= PSIZE); +} diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h new file mode 100644 index 000000000..12ae81bce --- /dev/null +++ b/sound/sources/audiere_source.h @@ -0,0 +1,51 @@ +#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H +#define MANGLE_SOUND_AUDIERE_SOURCE_H + +#include "../source.h" + +#include + +namespace Mangle { +namespace Sound { + +/// A sample source that decodes files using Audiere +class AudiereSource : public SampleSource +{ + audiere::SampleSourcePtr sample; + + // Number of bytes we cache between reads. This should correspond to + // the maximum possible value of frameSize. + static const int PSIZE = 10; + + // Size of one frame, in bytes + int frameSize; + + // Temporary storage for unevenly read samples. See the comment for + // read() in the .cpp file. + char pullOver[PSIZE]; + // How much of the above buffer is in use + int pullSize; + + void getFormat(); + + public: + /// Decode the given sound file + AudiereSource(const std::string &file); + + /// Decode the given sound stream + AudiereSource(Stream::Stream *src); + + /// Read directly from an existing audiere::SampleSource + AudiereSource(audiere::SampleSourcePtr src); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); +}; + +#include "loadertemplate.h" + +/// A factory that loads AudiereSources from file and stream +typedef SSL_Template AudiereLoader; + +}} // Namespace +#endif diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp new file mode 100644 index 000000000..d53b1ced3 --- /dev/null +++ b/sound/sources/ffmpeg_source.cpp @@ -0,0 +1,238 @@ +// NOT UPDATED - WIP + +#include "input_ffmpeg.h" +#include + +using namespace Mangle::Sound; + +// Static output buffer. Not thread safe, but supports multiple +// streams operated from the same thread. +static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; +bool FFM_InputManager::init = false; + +/// FFmpeg exception. +class FFM_Exception : public std::exception +{ + std::string msg; + + public: + + FFM_Exception(const std::string &m); + ~FFM_Exception() throw(); + virtual const char* what() const throw(); +}; + +FFM_Exception::FFM_Exception(const std::string &m) + : msg(m) {} + +const char* FFM_Exception::what() const throw() +{ return msg.c_str(); } + +FFM_Exception::~FFM_Exception() throw() {} + +static void fail(const std::string &msg) +{ + throw FFM_Exception("FFMpeg exception: " + msg); +} + + +// --- Manager --- + +FFM_InputManager::FFM_InputManager() +{ + if(!init) + { + av_register_all(); + av_log_set_level(AV_LOG_ERROR); + init = true; + } + + canLoadStream = false; +} + +InputSource *FFM_InputManager::load(const std::string &file) +{ return new FFM_InputSource(file); } + + +// --- Source --- + +FFM_InputSource::FFM_InputSource(const std::string &file) +{ + // FFmpeg doesn't handle several instances from one source. So we + // just store the filename. + name = file; +} + +InputStream *FFM_InputSource::getStream() +{ return new FFM_InputStream(name); } + +void FFM_InputSource::drop() +{ delete this; } + + +// --- Stream --- + +FFM_InputStream::FFM_InputStream(const std::string &file) +{ + std::string msg; + AVCodec *codec; + + empty = false; + + if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) + fail("Error loading audio file " + file); + + if(av_find_stream_info(FmtCtx) < 0) + { + msg = "Error in file stream " + file; + goto err; + } + + // Pick the first audio stream, if any + for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++) + { + // Pick the first audio stream + if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO) + break; + } + + if(StreamNum == FmtCtx->nb_streams) + fail("File " + file + " didn't contain any audio streams"); + + // Open the decoder + CodecCtx = FmtCtx->streams[StreamNum]->codec; + codec = avcodec_find_decoder(CodecCtx->codec_id); + + if(!codec || avcodec_open(CodecCtx, codec) < 0) + { + msg = "Error loading " + file + ": "; + if(codec) + msg += "coded error"; + else + msg += "no codec found"; + goto err; + } + + // No errors, we're done + return; + + // Handle errors + err: + av_close_input_file(FmtCtx); + fail(msg); +} + +FFM_InputStream::~FFM_InputStream() +{ + avcodec_close(CodecCtx); + av_close_input_file(FmtCtx); +} + +void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + if(rate) *rate = CodecCtx->sample_rate; + if(channels) *channels = CodecCtx->channels; + if(bits) *bits = 16; +} + +uint32_t FFM_InputStream::getData(void *data, uint32_t length) +{ + if(empty) return 0; + + uint32_t left = length; + uint8_t *outPtr = (uint8_t*)data; + + // First, copy over any stored data we might be sitting on + { + int s = storage.size(); + int copy = s; + if(s) + { + // Make sure there's room + if(copy > left) + copy = left; + + // Copy + memcpy(outPtr, &storage[0], copy); + outPtr += copy; + left -= copy; + + // Is there anything left in the storage? + s -= copy; + if(s) + { + assert(left == 0); + + // Move it to the start and resize + memmove(&storage[0], &storage[copy], s); + storage.resize(s); + } + } + } + + // Next, get more input data from stream, and decode it + while(left) + { + AVPacket packet; + + // Get the next packet, if any + if(av_read_frame(FmtCtx, &packet) < 0) + break; + + // We only allow one stream per file at the moment + assert(StreamNum == packet.stream_index); + + // Decode the packet + int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; + int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf, + &len, packet.data, packet.size); + assert(tmp < 0 || tmp == packet.size); + + // We don't need the input packet any longer + av_free_packet(&packet); + + if(tmp < 0) + fail("Error decoding audio stream"); + + // Copy whatever data we got, and advance the pointer + if(len > 0) + { + // copy = how many bytes do we copy now + int copy = len; + if(copy > left) + copy = left; + + // len = how many bytes are left uncopied + len -= copy; + + // copy data + memcpy(outPtr, outBuf, copy); + + // left = how much space is left in the caller output + // buffer + left -= copy; + outPtr += copy; + assert(left >= 0); + + if(len > 0) + { + // There were uncopied bytes. Store them for later. + assert(left == 0); + storage.resize(len); + memcpy(&storage[0], outBuf, len); + } + } + } + + // End of loop. Return the number of bytes copied. + assert(left <= length); + + // If we're returning less than asked for, then we're done + if(left > 0) + empty = true; + + return length - left; +} + +void FFM_InputStream::drop() +{ delete this; } diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h new file mode 100644 index 000000000..bf32084fa --- /dev/null +++ b/sound/sources/ffmpeg_source.h @@ -0,0 +1,47 @@ +#ifndef MANGLE_SOUND_FFMPEG_H +#define MANGLE_SOUND_FFMPEG_H + +#include "../input.h" +#include +#include +#include + +extern "C" +{ +#include +#include +} + +namespace Mangle { +namespace Sound { + +class FFMpegSource : public SampleSource +{ + AVFormatContext *FmtCtx; + AVCodecContext *CodecCtx; + int StreamNum; + bool empty; + + std::vector storage; + + public: + /// Decode the given sound file + FFMpegSource(const std::string &file); + + /// Decode the given sound stream (not supported by FFmpeg) + FFMpegSource(Stream::Stream *src) { assert(0); } + + ~FFMpegSource(); + + // Overrides + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); +}; + +#include "loadertemplate.h" + +/// A factory that loads FFMpegSources from file +typedef SSL_Template AudiereLoader; + +}} // namespaces +#endif diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h new file mode 100644 index 000000000..95d0e798c --- /dev/null +++ b/sound/sources/loadertemplate.h @@ -0,0 +1,26 @@ +#ifndef SSL_TEMPL_H +#define SSL_TEMPL_H + +template +class SSL_Template : public SampleSourceLoader +{ + SSL_Template() + { + canLoadStream = stream; + canLoadFile = file; + } + + SampleSource *load(const std::string &file) + { + assert(canLoadFile); + return new X(file); + } + + SampleSource *load(Stream::Stream *input) + { + assert(canLoadStream); + return new X(input); + } +}; + +#endif From 6281685f732f799f25565be8e431f1fab7ee9730 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 10:34:40 +0100 Subject: [PATCH 018/111] Finished source/ --- sound/sources/ffmpeg_source.cpp | 56 +++++++++------------------------ sound/sources/ffmpeg_source.h | 10 ++++-- 2 files changed, 22 insertions(+), 44 deletions(-) diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index d53b1ced3..af785eaa3 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,14 +1,10 @@ -// NOT UPDATED - WIP - #include "input_ffmpeg.h" -#include using namespace Mangle::Sound; // Static output buffer. Not thread safe, but supports multiple // streams operated from the same thread. static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; -bool FFM_InputManager::init = false; /// FFmpeg exception. class FFM_Exception : public std::exception @@ -35,44 +31,23 @@ static void fail(const std::string &msg) throw FFM_Exception("FFMpeg exception: " + msg); } +// --- Loader --- -// --- Manager --- +static bool init = false; -FFM_InputManager::FFM_InputManager() +FFMpegLoader::FFMpegLoader(bool setup) { - if(!init) + if(setup && !init) { av_register_all(); av_log_set_level(AV_LOG_ERROR); init = true; } - - canLoadStream = false; } -InputSource *FFM_InputManager::load(const std::string &file) -{ return new FFM_InputSource(file); } - - // --- Source --- -FFM_InputSource::FFM_InputSource(const std::string &file) -{ - // FFmpeg doesn't handle several instances from one source. So we - // just store the filename. - name = file; -} - -InputStream *FFM_InputSource::getStream() -{ return new FFM_InputStream(name); } - -void FFM_InputSource::drop() -{ delete this; } - - -// --- Stream --- - -FFM_InputStream::FFM_InputStream(const std::string &file) +FFMpegSource::FFMpegSource(const std::string &file) { std::string msg; AVCodec *codec; @@ -97,7 +72,7 @@ FFM_InputStream::FFM_InputStream(const std::string &file) } if(StreamNum == FmtCtx->nb_streams) - fail("File " + file + " didn't contain any audio streams"); + fail("File '" + file + "' didn't contain any audio streams"); // Open the decoder CodecCtx = FmtCtx->streams[StreamNum]->codec; @@ -105,7 +80,7 @@ FFM_InputStream::FFM_InputStream(const std::string &file) if(!codec || avcodec_open(CodecCtx, codec) < 0) { - msg = "Error loading " + file + ": "; + msg = "Error loading '" + file + "': "; if(codec) msg += "coded error"; else @@ -122,24 +97,24 @@ FFM_InputStream::FFM_InputStream(const std::string &file) fail(msg); } -FFM_InputStream::~FFM_InputStream() +FFMpegSource::~FFMpegSource() { avcodec_close(CodecCtx); av_close_input_file(FmtCtx); } -void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +void FFMpegSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) { if(rate) *rate = CodecCtx->sample_rate; if(channels) *channels = CodecCtx->channels; if(bits) *bits = 16; } -uint32_t FFM_InputStream::getData(void *data, uint32_t length) +size_t FFMpegSource::read(void *data, size_t length) { - if(empty) return 0; + if(isEof) return 0; - uint32_t left = length; + size_t left = length; uint8_t *outPtr = (uint8_t*)data; // First, copy over any stored data we might be sitting on @@ -209,7 +184,7 @@ uint32_t FFM_InputStream::getData(void *data, uint32_t length) memcpy(outPtr, outBuf, copy); // left = how much space is left in the caller output - // buffer + // buffer. This loop repeats as long left is > 0 left -= copy; outPtr += copy; assert(left >= 0); @@ -229,10 +204,7 @@ uint32_t FFM_InputStream::getData(void *data, uint32_t length) // If we're returning less than asked for, then we're done if(left > 0) - empty = true; + isEof = true; return length - left; } - -void FFM_InputStream::drop() -{ delete this; } diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h index bf32084fa..5317c11c0 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.h @@ -20,7 +20,6 @@ class FFMpegSource : public SampleSource AVFormatContext *FmtCtx; AVCodecContext *CodecCtx; int StreamNum; - bool empty; std::vector storage; @@ -41,7 +40,14 @@ class FFMpegSource : public SampleSource #include "loadertemplate.h" /// A factory that loads FFMpegSources from file -typedef SSL_Template AudiereLoader; +class FFMpegLoader : public SSL_Template +{ + public: + + /// Sets up the libavcodec library. If you want to do your own + /// setup, send a setup=false parameter. + FFMpegLoader(bool setup=true); +}; }} // namespaces #endif From 4ee198d66c8b54527fa35e1eb55e127f33c1a8a4 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 11:56:14 +0100 Subject: [PATCH 019/111] Added SampleBuffer (experimental) --- sound/buffer.h | 57 ++++++++++++++++++++++++++++++++++ sound/sources/loadertemplate.h | 6 ++-- sound/sources/memsource.h | 49 +++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 sound/buffer.h create mode 100644 sound/sources/memsource.h diff --git a/sound/buffer.h b/sound/buffer.h new file mode 100644 index 000000000..9cb27bd9c --- /dev/null +++ b/sound/buffer.h @@ -0,0 +1,57 @@ +#ifndef MANGLE_SOUND_BUFFER_H +#define MANGLE_SOUND_BUFFER_H + +#include "source.h" +#include "sources/memsource.h" +#include +#include + +namespace Mangle { +namespace Sound { + +/** A sample buffer is a factory that creates SampleSources from one + single sound source. It is helpful when you have many instances of + one sound and want to use one shared memory buffer. + + This is just a helper class - you don't have to include it in your + program if you don't need it. +*/ +class SampleBuffer +{ + std::vector buffer; + + public: + /// Reads the source into a memory buffer. Not heavily optimized. + SampleBuffer(SampleSource *source) + { + size_t final = 0; + + while(!source->eof()) + { + const int add = 16*1024; + + // Allocate more memory + size_t newSize = final + add; + buffer.resize(newSize); + + // Fill in data + size_t read = source->read(&buffer[final], add); + + // If we couldn't read enough data, we should be at the end + // of the stream + assert(read == add || source->eof()); + + final += read; + } + + // Downsize the buffer to the actual length + buffer.resize(final); + } + + /// Get a new source + SampleSource *get() + { return new MemorySource(&buffer[0], buffer.size()); } +}; + +}} +#endif diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h index 95d0e798c..0b668fffa 100644 --- a/sound/sources/loadertemplate.h +++ b/sound/sources/loadertemplate.h @@ -1,7 +1,7 @@ #ifndef SSL_TEMPL_H #define SSL_TEMPL_H -template +template class SSL_Template : public SampleSourceLoader { SSL_Template() @@ -13,13 +13,13 @@ class SSL_Template : public SampleSourceLoader SampleSource *load(const std::string &file) { assert(canLoadFile); - return new X(file); + return new SourceT(file); } SampleSource *load(Stream::Stream *input) { assert(canLoadStream); - return new X(input); + return new SourceT(input); } }; diff --git a/sound/sources/memsource.h b/sound/sources/memsource.h new file mode 100644 index 000000000..1e38d9052 --- /dev/null +++ b/sound/sources/memsource.h @@ -0,0 +1,49 @@ +#ifndef MANGLE_SOUND_MEMSOURCE_H +#define MANGLE_SOUND_MEMSOURCE_H + +#include "../source.h" + +namespace Mangle { +namespace Sound { + +/// A sample source reading directly from a memory buffer +class MemorySource : public SampleSource +{ + char *buf; + size_t len; + size_t pos; + + int32_t rate, channels, bits; + + public: + MemorySource(void *_buf, size_t _len, int32_t _rate, int32_t _channels, int32_t _bits) + : len(_len), pos(0), rate(_rate), channels(_channels), bits(_bits) + { buf = (char*)_buf; } + + /// Get the sample rate, number of channels, and bits per + /// sample. NULL parameters are ignored. + void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) const + { + if(_rate) *_rate = rate; + if(_channels) *_channels = channels; + if(_bits) *_bits = bits; + } + + bool eof() const { return pos == len; } + + size_t read(void *out, size_t count) + { + assert(len >= pos); + + if(count > (len-pos)) + count = len-pos; + + if(count) memcpy(out, buf+pos, count); + pos += count; + + return count; + } +}; + +}} // namespaces +#endif From fb88d9ef0e3ec4750cd2f8eca1c01a3db13f7377 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 13:12:48 +0100 Subject: [PATCH 020/111] Deleted a bunch of files, started on output --- sound/input.h | 83 -------- sound/{sound.h => output.h} | 107 +++------- sound/servers/.gitignore | 1 - sound/servers/audiere_imp.cpp | 95 --------- sound/servers/audiere_imp.h | 77 ------- sound/servers/input_audiere.cpp | 139 ------------- sound/servers/input_audiere.h | 59 ------ sound/servers/input_ffmpeg.cpp | 224 -------------------- sound/servers/input_ffmpeg.h | 75 ------- sound/servers/input_filter.h | 80 -------- sound/servers/openal_audiere.h | 29 --- sound/servers/openal_ffmpeg.h | 28 --- sound/servers/output_openal.cpp | 353 -------------------------------- sound/servers/output_openal.h | 125 ----------- 14 files changed, 34 insertions(+), 1441 deletions(-) delete mode 100644 sound/input.h rename sound/{sound.h => output.h} (52%) delete mode 100644 sound/servers/.gitignore delete mode 100644 sound/servers/audiere_imp.cpp delete mode 100644 sound/servers/audiere_imp.h delete mode 100644 sound/servers/input_audiere.cpp delete mode 100644 sound/servers/input_audiere.h delete mode 100644 sound/servers/input_ffmpeg.cpp delete mode 100644 sound/servers/input_ffmpeg.h delete mode 100644 sound/servers/input_filter.h delete mode 100644 sound/servers/openal_audiere.h delete mode 100644 sound/servers/openal_ffmpeg.h delete mode 100644 sound/servers/output_openal.cpp delete mode 100644 sound/servers/output_openal.h diff --git a/sound/input.h b/sound/input.h deleted file mode 100644 index f61a029ff..000000000 --- a/sound/input.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef MANGLE_SOUND_INPUT_H -#define MANGLE_SOUND_INPUT_H - -#include -#include - -#include "../stream/stream.h" - -namespace Mangle { -namespace Sound { - -/// An abstract interface for a read-once stream of audio data. -/** All instances of this is created through InputSource. Objects - should be manually deleted through a call to drop() when they are - no longer needed. -*/ -class InputStream -{ - public: - /// Get the sample rate, number of channels, and bits per - /// sample. NULL parameters are ignored. - virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0; - - /// Get decoded sound data from the stream. - /** Stores 'length' bytes (or less) in the buffer pointed to by - 'output'. Returns the number of bytes written. The function will - only return less than 'length' at the end of the stream. When - the stream is empty, all subsequent calls will return zero. - - @param output where to store data - @param length number of bytes to get - @return number of bytes actually written - */ - virtual uint32_t getData(void *output, uint32_t length) = 0; - - /// Kill this object - virtual void drop() = 0; - - /// Virtual destructor - virtual ~InputStream() {} -}; - -/// Abstract interface representing one sound source. -/** A sound source may represent one sound file or buffer, and is a - factory for producing InputStream objects from that - sound. Instances of this class are created by an InputManager. All - instances should be deleted through drop() when they are no longer - needed. - */ -class InputSource -{ - public: - /// Create a stream from this sound - virtual InputStream *getStream() = 0; - - /// Kill this object - virtual void drop() = 0; - - /// Virtual destructor - virtual ~InputSource() {} -}; - -/// Main interface to a sound decoder backend. -/** An input manager is a factory of InputSource objects. - */ -class InputManager -{ - public: - /// If true, the stream version of load() works - bool canLoadStream; - - /// Load a sound input source from file - virtual InputSource *load(const std::string &file) = 0; - - /// Load a sound input source from stream (if canLoadStream is true) - virtual InputSource *load(Stream::Stream *input) = 0; - - /// Virtual destructor - virtual ~InputManager() {} -}; - -}} // namespaces -#endif diff --git a/sound/sound.h b/sound/output.h similarity index 52% rename from sound/sound.h rename to sound/output.h index 0a51e9f93..4cdac7bed 100644 --- a/sound/sound.h +++ b/sound/output.h @@ -1,21 +1,24 @@ -#ifndef MANGLE_SOUND_SOUND_H -#define MANGLE_SOUND_SOUND_H +#ifndef MANGLE_SOUND_OUTPUT_H +#define MANGLE_SOUND_OUTPUT_H #include -#include "input.h" +#include "source.h" #include "../stream/stream.h" namespace Mangle { namespace Sound { -/// Abstract interface for sound instances -/** This class represents one sound instance, which may be played, - stopped, paused and so on. Instances are created from the Sound - class. All instances must be terminated manually using the drop() - function when they are no longer in use. +/// Abstract interface for a single playable sound +/** This class represents one sound outlet, which may be played, + stopped, paused and so on. + + Sound instances are created from the SoundFactory class. Sounds + may be connected to a SampleSource or read directly from a file, + and they may support 3d sounds, looping and other features + depending on the capabilities of the backend system. */ -class Instance +class Sound { public: /// Play or resume the sound @@ -36,63 +39,26 @@ class Instance /// Set the position. May not have any effect on 2D sounds. virtual void setPos(float x, float y, float z) = 0; - /// Kill the current object - virtual void drop() = 0; - - /// Virtual destructor - virtual ~Instance() {} -}; - -/// Abstract interface for sound files or sources -/** This class acts as a factory for sound Instance objects. - Implementations may choose to store shared sound buffers or other - optimizations in subclasses of Sound. Objects of this class are - created through the Manager class. All objects of this class - should be terminated manually using the drop() function when they - are no longer in use. -*/ -class Sound -{ - public: - /** - @brief Create an instance of this sound - - See also the capability flags in the Manager class. - - @param is3d true if this the sound is to be 3d enabled - @param repeat true if the sound should loop - @return new Instance object - */ - virtual Instance *getInstance(bool is3d, bool repeat) = 0; - - // Some prefab functions - - /// Shortcut for creating 3D instances - Instance *get3D(bool loop=false) - { return getInstance(true, loop); } - /// Shortcut for creating 2D instances - Instance *get2D(bool loop=false) - { return getInstance(false, loop); } - - /// Kill the current object - virtual void drop() = 0; - /// Virtual destructor virtual ~Sound() {} }; -/// Abstract interface for the main sound manager class -/** The sound manager is used to load sound files and is a factory for - Sound objects. It is the main entry point to a given sound system - implementation. +/// Factory interface for creating Sound objects +/** The SoundFactory is the main entry point to a given sound output + system. It is used to create Sound objects, which may be connected + to a sound file or stream, and which may be individually played, + paused, and so on. The class also contains a set of public bools which describe the capabilities the particular system. These should be set by implementations (base classes) in their respective constructors. */ -class Manager +class SoundFactory { public: + /// Virtual destructor + virtual ~SoundFactory() {} + /** @brief If set to true, you should call update() regularly (every frame or so) on this sound manager. If false, update() should not be called. @@ -111,23 +77,21 @@ class Manager */ bool canRepeatStream; - /// true if we can load sounds directly from file + /// true if we can load sounds directly from file (containing encoded data) bool canLoadFile; - /// true if we can load sounds from an InputSource - bool canLoadSource; - - /// If true, we can lound sound files from a Stream + /// If true, we can lound sound files from a Stream (containing encoded data) bool canLoadStream; + /// true if we can load sounds from a SampleSource (containing raw data) + bool canLoadSource; + /** - @brief Load a sound from an input source. Only valid if + @brief Load a sound from a sample source. Only valid if canLoadSource is true. This function loads a sound from a given stream as defined by - InputSource and InputStream. The InputSource and all streams - created from it will be dropped when drop() is called on the - owning sound / instance. + SampleSource. @param input the input source @param stream true if the file should be streamed. @@ -135,10 +99,10 @@ class Manager large files, but they are not required to. @return a new Sound object */ - virtual Sound *load(InputSource *input, bool stream=false) = 0; + virtual Sound *load(SampleSource *input, bool stream=false) = 0; /** - @brief Load a sound directly from file. Only valid if canLoadStream + @brief Load a sound file from stream. Only valid if canLoadStream is true. @param input audio file stream @@ -159,10 +123,10 @@ class Manager /// Call this every frame if needsUpdate is true /** - Update function that should be called regularly (about every - frame in a normal game setting.) Implementions may use this to - fill streaming buffers and similar. Implementations that do not - need this should set needsUpdate to false. + This should be called regularly (about every frame in a normal + game setting.) Implementions may use this for filling streaming + buffers and similar tasks. Implementations that do not need this + should set needsUpdate to false. */ virtual void update() = 0; @@ -177,9 +141,6 @@ class Manager virtual void setListenerPos(float x, float y, float z, float fx, float fy, float fz, float ux, float uy, float uz) = 0; - - /// Virtual destructor - virtual ~Manager() {} }; }} // Namespaces diff --git a/sound/servers/.gitignore b/sound/servers/.gitignore deleted file mode 100644 index 8b1378917..000000000 --- a/sound/servers/.gitignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/sound/servers/audiere_imp.cpp b/sound/servers/audiere_imp.cpp deleted file mode 100644 index ecdb0580a..000000000 --- a/sound/servers/audiere_imp.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "audiere_imp.h" - -// Exception handling -class Audiere_Exception : public std::exception -{ - std::string msg; - - public: - - Audiere_Exception(const std::string &m) : msg(m) {} - ~Audiere_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - -static void fail(const std::string &msg) -{ - throw Audiere_Exception("Audiere exception: " + msg); -} - -using namespace audiere; -using namespace Mangle::Sound; - -AudiereManager::AudiereManager() -{ - needsUpdate = false; - has3D = false; - canRepeatStream = true; - canLoadFile = true; - canLoadSource = false; - canLoadStream = false; - - device = OpenDevice(""); - - if(device == NULL) - fail("Failed to open device"); -} - -// --- Manager --- - -Sound *AudiereManager::load(const std::string &file, bool stream) -{ return new AudiereSound(file, device, stream); } - - -// --- Sound --- - -AudiereSound::AudiereSound(const std::string &file, - AudioDevicePtr _device, - bool _stream) - : device(_device), stream(_stream) -{ - sample = OpenSampleSource(file.c_str()); - if(!sample) - fail("Couldn't load file " + file); - - buf = CreateSampleBuffer(sample); -} - -Instance *AudiereSound::getInstance(bool is3d, bool repeat) -{ - // Ignore is3d. Audiere doesn't implement 3d sound. We could make a - // hack software 3D implementation later, but it's not that - // important. - - SampleSourcePtr sample = buf->openStream(); - if(!sample) - fail("Failed to open sample stream"); - - OutputStreamPtr sound = OpenSound(device, sample, stream); - - if(repeat) - sound->setRepeat(true); - - return new AudiereInstance(sound); -} - - -// --- Instance --- - -AudiereInstance::AudiereInstance(OutputStreamPtr _sound) - : sound(_sound) {} - -void AudiereInstance::play() -{ sound->play(); } - -void AudiereInstance::stop() -{ sound->stop(); } - -void AudiereInstance::pause() -{ stop(); } - -bool AudiereInstance::isPlaying() -{ return sound->isPlaying(); } - -void AudiereInstance::setVolume(float vol) -{ sound->setVolume(vol); } diff --git a/sound/servers/audiere_imp.h b/sound/servers/audiere_imp.h deleted file mode 100644 index 5ebb812bd..000000000 --- a/sound/servers/audiere_imp.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef MANGLE_SOUND_AUDIERE_H -#define MANGLE_SOUND_AUDIERE_H - -#include "../sound.h" - -#include -#include - -namespace Mangle { -namespace Sound { - -/// Implementation of Sound::Manager for Audiere -class AudiereManager : public Manager -{ - audiere::AudioDevicePtr device; - - public: - AudiereManager(); - - virtual Sound *load(const std::string &file, bool stream=false); - - /// not implemented yet - virtual Sound *load(Stream::Stream *input, bool stream=false) - { assert(0); } - - /// disabled - virtual Sound *load(InputSource *input, bool stream=false) - { assert(0); } - /// disabled - virtual void update() { assert(0); } - /// disabled - virtual void setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) - { assert(0); }; -}; - -/// Audiere Sound implementation -class AudiereSound : public Sound -{ - audiere::AudioDevicePtr device; - audiere::SampleSourcePtr sample; - audiere::SampleBufferPtr buf; - - bool stream; - - public: - virtual Instance *getInstance(bool is3d, bool repeat); - virtual void drop() - { delete this; } - - AudiereSound(const std::string &file, audiere::AudioDevicePtr device, - bool stream); -}; - -/// Audiere Instance implementation -class AudiereInstance : public Instance -{ - audiere::OutputStreamPtr sound; - - public: - virtual void play(); - virtual void stop(); - virtual void pause(); - virtual bool isPlaying(); - virtual void setVolume(float); - /// disabled - virtual void setPos(float x, float y, float z) - { assert(0); } - virtual void drop() - { delete this; } - - AudiereInstance(audiere::OutputStreamPtr); -}; - -}} // Namespace -#endif diff --git a/sound/servers/input_audiere.cpp b/sound/servers/input_audiere.cpp deleted file mode 100644 index c48f45013..000000000 --- a/sound/servers/input_audiere.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "input_audiere.h" -#include - -#include "../../stream/clients/audiere_file.h" - -// Exception handling -class Audiere_Exception : public std::exception -{ - std::string msg; - - public: - - Audiere_Exception(const std::string &m) : msg(m) {} - ~Audiere_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - -static void fail(const std::string &msg) -{ - throw Audiere_Exception("Audiere exception: " + msg); -} - -using namespace audiere; -using namespace Mangle::Sound; - -// --- InputManager --- - -AudiereInput::AudiereInput() -{ - canLoadStream = true; -} - -InputSource *AudiereInput::load(const std::string &file) -{ return new AudiereSource(file); } - -InputSource *AudiereInput::load(Stream::Stream *input) -{ return new AudiereSource(input); } - -// --- InputSource --- - -AudiereSource::AudiereSource(const std::string &file) -{ - SampleSourcePtr sample = OpenSampleSource(file.c_str()); - if(!sample) - fail("Couldn't load file " + file); - - buf = CreateSampleBuffer(sample); -} - -AudiereSource::AudiereSource(Stream::Stream *input) -{ - SampleSourcePtr sample = OpenSampleSource - (new Stream::AudiereFile(input)); - if(!sample) - fail("Couldn't load stream"); - - buf = CreateSampleBuffer(sample); -} - -InputStream *AudiereSource::getStream() -{ - return new AudiereStream(buf->openStream()); -} - -// --- InputStream --- - -AudiereStream::AudiereStream(SampleSourcePtr _sample) - : sample(_sample), pullSize(0) -{ - assert(sample); - - SampleFormat fmt; - int channels, rate; - sample->getFormat(channels, rate, fmt); - - // Calculate the size of one frame - frameSize = GetSampleSize(fmt) * channels; - - // Make sure that our pullover hack will work. Increase this size if - // this doesn't work in all cases. - assert(frameSize <= PSIZE); -} - -void AudiereStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) -{ - SampleFormat fmt; - sample->getFormat(*channels, *rate, fmt); - if(fmt == SF_U8) - *bits = 8; - else if(fmt == SF_S16) - *bits = 16; - else assert(0); -} - -/* - Get data. Since Audiere operates with frames, not bytes, there's a - little conversion magic going on here. We need to make sure we're - reading a whole number of frames - if not, we need to store the - remainding part of the last frame and remember it for the next read - operation. - - */ -uint32_t AudiereStream::getData(void *_data, uint32_t length) -{ - char *data = (char*)_data; - - // Move the remains from the last operation first - if(pullSize) - { - // pullSize is how much was stored the last time, so skip that. - memcpy(data, pullOver+pullSize, PSIZE-pullSize); - length -= pullSize; - data += pullSize; - } - - // Determine the overshoot up front - pullSize = length % frameSize; - - // Number of whole frames - int frames = length / frameSize; - - // Read the data - int res = sample->read(frames, data); - - // Are we missing data? If resread(1, pullOver) != 0)) - { - // Now, move as much of it as we can fit into the output - // data - memcpy(data+length-pullSize, pullOver, pullSize); - } - else pullSize = 0; - - // Return the total number of bytes stored - return frameSize*res + pullSize; -} diff --git a/sound/servers/input_audiere.h b/sound/servers/input_audiere.h deleted file mode 100644 index e753b0174..000000000 --- a/sound/servers/input_audiere.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef MANGLE_SOUND_AUDIERE_INPUT_H -#define MANGLE_SOUND_AUDIERE_INPUT_H - -#include "../input.h" - -#include - -namespace Mangle { -namespace Sound { - -/// Implementation of Sound::InputManager for Audiere -class AudiereInput : public InputManager -{ - public: - AudiereInput(); - - /// Load a source from a file - InputSource *load(const std::string &file); - - /// Load a source from a stream - virtual InputSource *load(Stream::Stream *input); -}; - -/// Audiere InputSource implementation -class AudiereSource : public InputSource -{ - audiere::SampleBufferPtr buf; - - public: - AudiereSource(const std::string &file); - AudiereSource(Stream::Stream *input); - InputStream *getStream(); - void drop() { delete this; } -}; - -/// Audiere InputStream implementation -class AudiereStream : public InputStream -{ - audiere::SampleSourcePtr sample; - int frameSize; // Size of one frame, in bytes - - static const int PSIZE = 10; - - // Temporary storage for unevenly read samples. See the comment for - // getData() in the .cpp file. - char pullOver[PSIZE]; - // How much of the above buffer is in use - int pullSize; - - public: - AudiereStream(audiere::SampleSourcePtr _sample); - - void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); - uint32_t getData(void *data, uint32_t length); - void drop() { delete this; } -}; - -}} // Namespace -#endif diff --git a/sound/servers/input_ffmpeg.cpp b/sound/servers/input_ffmpeg.cpp deleted file mode 100644 index f29ddfdb6..000000000 --- a/sound/servers/input_ffmpeg.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include "input_ffmpeg.h" -#include - -using namespace Mangle::Sound; - -// Static output buffer. Not thread safe, but supports multiple -// streams operated from the same thread. -static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; -bool FFM_InputManager::init = false; - -FFM_Exception::FFM_Exception(const std::string &m) - : msg(m) {} - -const char* FFM_Exception::what() const throw() -{ return msg.c_str(); } - -FFM_Exception::~FFM_Exception() throw() {} - -static void fail(const std::string &msg) -{ - throw FFM_Exception("FFMpeg exception: " + msg); -} - - -// --- Manager --- - -FFM_InputManager::FFM_InputManager() -{ - if(!init) - { - av_register_all(); - av_log_set_level(AV_LOG_ERROR); - init = true; - } - - canLoadStream = false; -} - -InputSource *FFM_InputManager::load(const std::string &file) -{ return new FFM_InputSource(file); } - - -// --- Source --- - -FFM_InputSource::FFM_InputSource(const std::string &file) -{ - // FFmpeg doesn't handle several instances from one source. So we - // just store the filename. - name = file; -} - -InputStream *FFM_InputSource::getStream() -{ return new FFM_InputStream(name); } - -void FFM_InputSource::drop() -{ delete this; } - - -// --- Stream --- - -FFM_InputStream::FFM_InputStream(const std::string &file) -{ - std::string msg; - AVCodec *codec; - - empty = false; - - if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) - fail("Error loading audio file " + file); - - if(av_find_stream_info(FmtCtx) < 0) - { - msg = "Error in file stream " + file; - goto err; - } - - // Pick the first audio stream, if any - for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++) - { - // Pick the first audio stream - if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO) - break; - } - - if(StreamNum == FmtCtx->nb_streams) - fail("File " + file + " didn't contain any audio streams"); - - // Open the decoder - CodecCtx = FmtCtx->streams[StreamNum]->codec; - codec = avcodec_find_decoder(CodecCtx->codec_id); - - if(!codec || avcodec_open(CodecCtx, codec) < 0) - { - msg = "Error loading " + file + ": "; - if(codec) - msg += "coded error"; - else - msg += "no codec found"; - goto err; - } - - // No errors, we're done - return; - - // Handle errors - err: - av_close_input_file(FmtCtx); - fail(msg); -} - -FFM_InputStream::~FFM_InputStream() -{ - avcodec_close(CodecCtx); - av_close_input_file(FmtCtx); -} - -void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) -{ - if(rate) *rate = CodecCtx->sample_rate; - if(channels) *channels = CodecCtx->channels; - if(bits) *bits = 16; -} - -uint32_t FFM_InputStream::getData(void *data, uint32_t length) -{ - if(empty) return 0; - - uint32_t left = length; - uint8_t *outPtr = (uint8_t*)data; - - // First, copy over any stored data we might be sitting on - { - int s = storage.size(); - int copy = s; - if(s) - { - // Make sure there's room - if(copy > left) - copy = left; - - // Copy - memcpy(outPtr, &storage[0], copy); - outPtr += copy; - left -= copy; - - // Is there anything left in the storage? - s -= copy; - if(s) - { - assert(left == 0); - - // Move it to the start and resize - memmove(&storage[0], &storage[copy], s); - storage.resize(s); - } - } - } - - // Next, get more input data from stream, and decode it - while(left) - { - AVPacket packet; - - // Get the next packet, if any - if(av_read_frame(FmtCtx, &packet) < 0) - break; - - // We only allow one stream per file at the moment - assert(StreamNum == packet.stream_index); - - // Decode the packet - int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; - int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf, - &len, packet.data, packet.size); - assert(tmp < 0 || tmp == packet.size); - - // We don't need the input packet any longer - av_free_packet(&packet); - - if(tmp < 0) - fail("Error decoding audio stream"); - - // Copy whatever data we got, and advance the pointer - if(len > 0) - { - // copy = how many bytes do we copy now - int copy = len; - if(copy > left) - copy = left; - - // len = how many bytes are left uncopied - len -= copy; - - // copy data - memcpy(outPtr, outBuf, copy); - - // left = how much space is left in the caller output - // buffer - left -= copy; - outPtr += copy; - assert(left >= 0); - - if(len > 0) - { - // There were uncopied bytes. Store them for later. - assert(left == 0); - storage.resize(len); - memcpy(&storage[0], outBuf, len); - } - } - } - - // End of loop. Return the number of bytes copied. - assert(left <= length); - - // If we're returning less than asked for, then we're done - if(left > 0) - empty = true; - - return length - left; -} - -void FFM_InputStream::drop() -{ delete this; } diff --git a/sound/servers/input_ffmpeg.h b/sound/servers/input_ffmpeg.h deleted file mode 100644 index b744a7585..000000000 --- a/sound/servers/input_ffmpeg.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef MANGLE_SOUND_FFMPEG_H -#define MANGLE_SOUND_FFMPEG_H - -#include "../input.h" -#include -#include -#include - -extern "C" -{ -#include -#include -} - -namespace Mangle { -namespace Sound { - -/// FFmpeg exception -class FFM_Exception : public std::exception -{ - std::string msg; - - public: - - FFM_Exception(const std::string &m); - ~FFM_Exception() throw(); - virtual const char* what() const throw(); -}; - -/// FFMpeg implementation of InputManager -class FFM_InputManager : public InputManager -{ - static bool init; - - public: - FFM_InputManager(); - virtual InputSource *load(const std::string &file); - - /// not supported - virtual InputSource *load(Stream::Stream *input) { assert(0); } -}; - -/// FFMpeg implementation of InputSource -class FFM_InputSource : public InputSource -{ - std::string name; - - public: - FFM_InputSource(const std::string &file); - - virtual InputStream *getStream(); - virtual void drop(); -}; - -/// FFMpeg implementation of InputStream -class FFM_InputStream : public InputStream -{ - AVFormatContext *FmtCtx; - AVCodecContext *CodecCtx; - int StreamNum; - bool empty; - - std::vector storage; - - public: - FFM_InputStream(const std::string &file); - ~FFM_InputStream(); - - virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); - virtual uint32_t getData(void *data, uint32_t length); - virtual void drop(); -}; - -}} // namespaces -#endif diff --git a/sound/servers/input_filter.h b/sound/servers/input_filter.h deleted file mode 100644 index 455a60e14..000000000 --- a/sound/servers/input_filter.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef MANGLE_INPUT_FILTER_H -#define MANGLE_INPUT_FILTER_H - -#include "../sound.h" - -#include - -namespace Mangle { -namespace Sound { - -/** - @brief This filter class adds file loading capabilities to a - Sound::Manager class, by associating an InputManager with it. - - The class takes an existing Manager able to load streams, and - associates an InputManager with it. The combined class is able to - load files directly. - - Example: - \code - - // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot - // decode sound files on its own. - InputFilter mg(new OpenAL_Manager, new FFM_InputManager); - - // We can now load filenames directly. - mg.load("file1.mp3"); - \endcode -*/ -class InputFilter : public Manager -{ - protected: - Manager *snd; - InputManager *inp; - - public: - /// Empty constructor - InputFilter() {} - - /// Assign an input manager and a sound manager to this object - InputFilter(Manager *_snd, InputManager *_inp) - { set(_snd, _inp); } - - /// Assign an input manager and a sound manager to this object - void set(Manager *_snd, InputManager *_inp) - { - inp = _inp; - snd = _snd; - - // Set capabilities - needsUpdate = snd->needsUpdate; - has3D = snd->has3D; - canRepeatStream = snd->canRepeatStream; - canLoadStream = inp->canLoadStream; - - // Both these should be true, or the use of this class is pretty - // pointless - canLoadSource = snd->canLoadSource; - canLoadFile = canLoadSource; - assert(canLoadSource && canLoadFile); - } - - virtual Sound *load(const std::string &file, bool stream=false) - { return load(inp->load(file), stream); } - - virtual Sound *load(Stream::Stream *input, bool stream=false) - { return load(inp->load(input), stream); } - - virtual Sound *load(InputSource *input, bool stream=false) - { return snd->load(input, stream); } - - virtual void update() { snd->update(); } - virtual void setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) - { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); } -}; - -}} -#endif diff --git a/sound/servers/openal_audiere.h b/sound/servers/openal_audiere.h deleted file mode 100644 index 65947b22f..000000000 --- a/sound/servers/openal_audiere.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MANGLE_FFMPEG_OPENAL_H -#define MANGLE_FFMPEG_OPENAL_H - -#include "input_filter.h" -#include "input_audiere.h" -#include "output_openal.h" - -namespace Mangle { -namespace Sound { - -/// A InputFilter that adds audiere decoding to OpenAL. Audiere has -/// it's own output, but OpenAL sports 3D and other advanced features. -class OpenAL_Audiere_Manager : public InputFilter -{ - public: - OpenAL_Audiere_Manager() - { - set(new OpenAL_Manager, - new AudiereInput); - } - ~OpenAL_Audiere_Manager() - { - delete snd; - delete inp; - } -}; - -}} -#endif diff --git a/sound/servers/openal_ffmpeg.h b/sound/servers/openal_ffmpeg.h deleted file mode 100644 index 179d3cb70..000000000 --- a/sound/servers/openal_ffmpeg.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MANGLE_FFMPEG_OPENAL_H -#define MANGLE_FFMPEG_OPENAL_H - -#include "input_filter.h" -#include "input_ffmpeg.h" -#include "output_openal.h" - -namespace Mangle { -namespace Sound { - -/// An InputFilter that adds FFmpeg decoding to OpenAL -class OpenAL_FFM_Manager : public InputFilter -{ - public: - OpenAL_FFM_Manager() - { - set(new OpenAL_Manager, - new FFM_InputManager); - } - ~OpenAL_FFM_Manager() - { - delete snd; - delete inp; - } -}; - -}} -#endif diff --git a/sound/servers/output_openal.cpp b/sound/servers/output_openal.cpp deleted file mode 100644 index 06a8edca8..000000000 --- a/sound/servers/output_openal.cpp +++ /dev/null @@ -1,353 +0,0 @@ -#include "output_openal.h" -#include - -#include - -using namespace Mangle::Sound; - - -// ---- Helper functions and classes ---- - -class OpenAL_Exception : public std::exception -{ - std::string msg; - - public: - - OpenAL_Exception(const std::string &m) : msg(m) {} - ~OpenAL_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - -static void fail(const std::string &msg) -{ - throw OpenAL_Exception("OpenAL exception: " + msg); -} - -static void checkALError(const std::string &msg) -{ - ALenum err = alGetError(); - if(err != AL_NO_ERROR) - fail("\"" + std::string(alGetString(err)) + "\" while " + msg); -} - -static void getALFormat(InputStream *inp, int &fmt, int &rate) -{ - int ch, bits; - inp->getInfo(&rate, &ch, &bits); - - fmt = 0; - - if(bits == 8) - { - if(ch == 1) fmt = AL_FORMAT_MONO8; - if(ch == 2) fmt = AL_FORMAT_STEREO8; - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); - if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8"); - } - } - if(bits == 16) - { - if(ch == 1) fmt = AL_FORMAT_MONO16; - if(ch == 2) fmt = AL_FORMAT_STEREO16; - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); - if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16"); - } - } - - if(fmt == 0) - fail("Unsupported input format"); -} - - -// ---- Manager ---- - -OpenAL_Manager::OpenAL_Manager() - : Context(NULL), Device(NULL) -{ - needsUpdate = true; - has3D = true; - canRepeatStream = false; - canLoadFile = false; - canLoadSource = true; - canLoadStream = false; - - // Set up sound system - Device = alcOpenDevice(NULL); - Context = alcCreateContext(Device, NULL); - - if(!Device || !Context) - fail("Failed to initialize context or device"); - - alcMakeContextCurrent(Context); -} - -OpenAL_Manager::~OpenAL_Manager() -{ - // Deinitialize sound system - alcMakeContextCurrent(NULL); - if(Context) alcDestroyContext(Context); - if(Device) alcCloseDevice(Device); -} - -Sound *OpenAL_Manager::load(const std::string &file, bool stream) -{ assert(0 && "OpenAL cannot decode files"); } - -Sound *OpenAL_Manager::load(Stream::Stream*,bool) -{ assert(0 && "OpenAL cannot decode streams"); } - -Sound *OpenAL_Manager::load(InputSource *source, bool stream) -{ return new OpenAL_Sound(source, this, stream); } - -void OpenAL_Manager::update() -{ - // Loop through all the streaming sounds and update them - LST::iterator it, next; - for(it = streaming.begin(); - it != streaming.end(); - it++) - { - (*it)->update(); - } -} - -void OpenAL_Manager::setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) -{ - ALfloat orient[6]; - orient[0] = fx; - orient[1] = fy; - orient[2] = fz; - orient[3] = ux; - orient[4] = uy; - orient[5] = uz; - alListener3f(AL_POSITION, x, y, z); - alListenerfv(AL_ORIENTATION, orient); - checkALError("setting listener position"); -} - -OpenAL_Manager::LST::iterator OpenAL_Manager::add_stream(OpenAL_Stream_Instance* inst) -{ - streaming.push_front(inst); - return streaming.begin(); -} - -void OpenAL_Manager::remove_stream(LST::iterator it) -{ - streaming.erase(it); -} - - -// ---- Sound ---- - -OpenAL_Sound::~OpenAL_Sound() -{ - // Kill the input source - if(source) source->drop(); - - // And any allocated buffers - if(bufferID) - alDeleteBuffers(1, &bufferID); -} - -Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) -{ - assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); - - if(stream) - return new OpenAL_Stream_Instance(source->getStream(), owner); - - // Load the buffer if it hasn't been done already - if(bufferID == 0) - { - // Get an input stream and load the file from it - InputStream *inp = source->getStream(); - - std::vector buffer; - - // Add 32 kb at each increment - const int ADD = 32*1024; - - // Fill the buffer. We increase the buffer until it's large - // enough to fit all the data. - while(true) - { - // Increase the buffer - int oldlen = buffer.size(); - buffer.resize(oldlen+ADD); - - // Read the data - size_t len = inp->getData(&buffer[oldlen], ADD); - - // If we read less than requested, we're done. - if(len < ADD) - { - // Downsize the buffer to the right size - buffer.resize(oldlen+len); - break; - } - } - - // Get the format - int fmt, rate; - getALFormat(inp, fmt, rate); - - // We don't need the file anymore - inp->drop(); - source->drop(); - source = NULL; - - // Move the data into OpenAL - alGenBuffers(1, &bufferID); - alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); - checkALError("loading sound buffer"); - } // End of buffer loading - - // At this point, the file data has been loaded into the buffer - // in 'bufferID', and we should be ready to go. - assert(bufferID != 0); - - return new OpenAL_Simple_Instance(bufferID); -} - - -// ---- OpenAL_Instance_Base ---- - -void OpenAL_Instance_Base::play() -{ - alSourcePlay(inst); - checkALError("starting playback"); -} - -void OpenAL_Instance_Base::stop() -{ - alSourceStop(inst); - checkALError("stopping"); -} - -void OpenAL_Instance_Base::pause() -{ - alSourcePause(inst); - checkALError("pausing"); -} - -bool OpenAL_Instance_Base::isPlaying() -{ - ALint state; - alGetSourcei(inst, AL_SOURCE_STATE, &state); - - return state == AL_PLAYING; -} - -void OpenAL_Instance_Base::setVolume(float volume) -{ - if(volume > 1.0) volume = 1.0; - if(volume < 0.0) volume = 0.0; - alSourcef(inst, AL_GAIN, volume); - checkALError("setting volume"); -} - -void OpenAL_Instance_Base::setPos(float x, float y, float z) -{ - alSource3f(inst, AL_POSITION, x, y, z); - checkALError("setting position"); -} - - -// ---- OpenAL_Simple_Instance ---- - -OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) -{ - // Create instance and associate buffer - alGenSources(1, &inst); - alSourcei(inst, AL_BUFFER, buf); -} - -OpenAL_Simple_Instance::~OpenAL_Simple_Instance() -{ - // Stop - alSourceStop(inst); - - // Return sound - alDeleteSources(1, &inst); -} - - -// ---- OpenAL_Stream_Instance ---- - -OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, - OpenAL_Manager *_owner) - : stream(_stream), owner(_owner) -{ - // Deduce the file format from the stream info - getALFormat(stream, fmt, rate); - - // Create the buffers and the sound instance - alGenBuffers(BUFS, bufs); - alGenSources(1, &inst); - - checkALError("initializing"); - - // Fill the buffers and que them - for(int i=0; iadd_stream(this); -} - -void OpenAL_Stream_Instance::queueBuffer(ALuint bId) -{ - char buf[SIZE]; - - // Get the data - int len = stream->getData(buf, SIZE); - if(len == 0) - return; - - // .. and stash it - alBufferData(bId, fmt, buf, len, rate); - alSourceQueueBuffers(inst, 1, &bId); -} - -OpenAL_Stream_Instance::~OpenAL_Stream_Instance() -{ - // Remove ourselves from streaming list - owner->remove_stream(lit); - - // Stop - alSourceStop(inst); - - // Kill the input stream - stream->drop(); - - // Return sound - alDeleteSources(1, &inst); - - // Delete buffers - alDeleteBuffers(BUFS, bufs); -} - -void OpenAL_Stream_Instance::update() -{ - ALint count; - alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); - - for(int i = 0;i < count;i++) - { - // Unque a finished buffer - ALuint bId; - alSourceUnqueueBuffers(inst, 1, &bId); - - // Queue a new buffer - queueBuffer(bId); - } -} diff --git a/sound/servers/output_openal.h b/sound/servers/output_openal.h deleted file mode 100644 index 53226c32f..000000000 --- a/sound/servers/output_openal.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef MANGLE_SOUND_OPENAL_H -#define MANGLE_SOUND_OPENAL_H - -#include "../sound.h" - -#include -#include -#include - -namespace Mangle { -namespace Sound { - -class OpenAL_Stream_Instance; - -/// OpenAL implementation of Manager -class OpenAL_Manager : public Manager -{ -public: - // List of all streaming sounds - these need to be updated regularly - typedef std::list LST; - - OpenAL_Manager(); - virtual ~OpenAL_Manager(); - - LST::iterator add_stream(OpenAL_Stream_Instance*); - void remove_stream(LST::iterator); - - virtual Sound *load(const std::string &file, bool stream=false); - virtual Sound *load(Stream::Stream *input, bool stream=false); - virtual Sound *load(InputSource* input, bool stream=false); - virtual void update(); - virtual void setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz); - - private: - ALCdevice *Device; - ALCcontext *Context; - - LST streaming; -}; - -/// OpenAL implementation of Sound -class OpenAL_Sound : public Sound -{ - InputSource *source; - OpenAL_Manager *owner; - bool stream; - - // Used for non-streaming files, contains the entire sound buffer if - // non-zero - ALuint bufferID; - - public: - OpenAL_Sound(InputSource *src, OpenAL_Manager *own, bool str) - : source(src), owner(own), stream(str), bufferID(0) {} - ~OpenAL_Sound(); - - virtual Instance *getInstance(bool is3d, bool repeat); - void drop() { delete this; } -}; - -/// Shared parent class that holds an OpenAL sound instance. Just used -/// for shared functionality, has no setup or cleanup code. -class OpenAL_Instance_Base : public Instance -{ - protected: - ALuint inst; - - public: - void drop() { delete this; } - virtual void play(); - virtual void stop(); - virtual void pause(); - virtual bool isPlaying(); - virtual void setVolume(float); - virtual void setPos(float x, float y, float z); -}; - -/// Non-streaming OpenAL-implementation of Instance. Uses a shared -/// sound buffer in OpenAL_Sound. -class OpenAL_Simple_Instance : public OpenAL_Instance_Base -{ - public: - OpenAL_Simple_Instance(ALuint buf); - ~OpenAL_Simple_Instance(); -}; - -/// Streaming OpenAL-implementation of Instance. -class OpenAL_Stream_Instance : public OpenAL_Instance_Base -{ - // Since OpenAL streams have to be updated manually each frame, we - // need to have a sufficiently large buffer so that we don't run out - // of data in the mean time. Each instance will take around 512 Kb - // of memory, independent of how large the file is. - static const int BUFS = 4; - static const int SIZE = 128*1024; - - // Buffers - ALuint bufs[BUFS]; - - // Sound format settings - int rate, fmt; - - // Source of data - InputStream *stream; - - OpenAL_Manager *owner; - - // List iterator, used for removing ourselves from the streaming - // list when we're deleted. - OpenAL_Manager::LST::iterator lit; - - // Load and queue a new buffer - void queueBuffer(ALuint buffer); - -public: - OpenAL_Stream_Instance(InputStream*, OpenAL_Manager*); - ~OpenAL_Stream_Instance(); - - void update(); -}; - -}} // namespaces -#endif From c8256e3bb31f161937b47cadbcffe9b8f3dbf016 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 15:54:05 +0100 Subject: [PATCH 021/111] Worked on outputs, OpenAL. VERY wip --- sound/outputs/input_filter.h | 80 ++++++++++ sound/outputs/openal_audiere.h | 29 ++++ sound/outputs/openal_out.cpp | 283 +++++++++++++++++++++++++++++++++ sound/outputs/openal_out.h | 109 +++++++++++++ 4 files changed, 501 insertions(+) create mode 100644 sound/outputs/input_filter.h create mode 100644 sound/outputs/openal_audiere.h create mode 100644 sound/outputs/openal_out.cpp create mode 100644 sound/outputs/openal_out.h diff --git a/sound/outputs/input_filter.h b/sound/outputs/input_filter.h new file mode 100644 index 000000000..455a60e14 --- /dev/null +++ b/sound/outputs/input_filter.h @@ -0,0 +1,80 @@ +#ifndef MANGLE_INPUT_FILTER_H +#define MANGLE_INPUT_FILTER_H + +#include "../sound.h" + +#include + +namespace Mangle { +namespace Sound { + +/** + @brief This filter class adds file loading capabilities to a + Sound::Manager class, by associating an InputManager with it. + + The class takes an existing Manager able to load streams, and + associates an InputManager with it. The combined class is able to + load files directly. + + Example: + \code + + // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot + // decode sound files on its own. + InputFilter mg(new OpenAL_Manager, new FFM_InputManager); + + // We can now load filenames directly. + mg.load("file1.mp3"); + \endcode +*/ +class InputFilter : public Manager +{ + protected: + Manager *snd; + InputManager *inp; + + public: + /// Empty constructor + InputFilter() {} + + /// Assign an input manager and a sound manager to this object + InputFilter(Manager *_snd, InputManager *_inp) + { set(_snd, _inp); } + + /// Assign an input manager and a sound manager to this object + void set(Manager *_snd, InputManager *_inp) + { + inp = _inp; + snd = _snd; + + // Set capabilities + needsUpdate = snd->needsUpdate; + has3D = snd->has3D; + canRepeatStream = snd->canRepeatStream; + canLoadStream = inp->canLoadStream; + + // Both these should be true, or the use of this class is pretty + // pointless + canLoadSource = snd->canLoadSource; + canLoadFile = canLoadSource; + assert(canLoadSource && canLoadFile); + } + + virtual Sound *load(const std::string &file, bool stream=false) + { return load(inp->load(file), stream); } + + virtual Sound *load(Stream::Stream *input, bool stream=false) + { return load(inp->load(input), stream); } + + virtual Sound *load(InputSource *input, bool stream=false) + { return snd->load(input, stream); } + + virtual void update() { snd->update(); } + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); } +}; + +}} +#endif diff --git a/sound/outputs/openal_audiere.h b/sound/outputs/openal_audiere.h new file mode 100644 index 000000000..65947b22f --- /dev/null +++ b/sound/outputs/openal_audiere.h @@ -0,0 +1,29 @@ +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H + +#include "input_filter.h" +#include "input_audiere.h" +#include "output_openal.h" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds audiere decoding to OpenAL. Audiere has +/// it's own output, but OpenAL sports 3D and other advanced features. +class OpenAL_Audiere_Manager : public InputFilter +{ + public: + OpenAL_Audiere_Manager() + { + set(new OpenAL_Manager, + new AudiereInput); + } + ~OpenAL_Audiere_Manager() + { + delete snd; + delete inp; + } +}; + +}} +#endif diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp new file mode 100644 index 000000000..5d3633e75 --- /dev/null +++ b/sound/outputs/openal_out.cpp @@ -0,0 +1,283 @@ +#include "openal_out.h" +#include + +// ALuint bufferID; + +using namespace Mangle::Sound; + +// ---- Helper functions and classes ---- + +class OpenAL_Exception : public std::exception +{ + std::string msg; + + public: + + OpenAL_Exception(const std::string &m) : msg(m) {} + ~OpenAL_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw OpenAL_Exception("OpenAL exception: " + msg); +} + +static void checkALError(const std::string &msg) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + fail("\"" + std::string(alGetString(err)) + "\" while " + msg); +} + +static void getALFormat(InputStream *inp, int &fmt, int &rate) +{ + int ch, bits; + inp->getInfo(&rate, &ch, &bits); + + fmt = 0; + + if(bits == 8) + { + if(ch == 1) fmt = AL_FORMAT_MONO8; + if(ch == 2) fmt = AL_FORMAT_STEREO8; + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8"); + } + } + if(bits == 16) + { + if(ch == 1) fmt = AL_FORMAT_MONO16; + if(ch == 2) fmt = AL_FORMAT_STEREO16; + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16"); + } + } + + if(fmt == 0) + fail("Unsupported input format"); +} + +// ---- OpenAL_Sound ---- + +OpenAL_Sound::OpenAL_Sound(SampleSource *input) +{ + +} + +void OpenAL_Sound::play() +{ + alSourcePlay(inst); + checkALError("starting playback"); +} + +void OpenAL_Sound::stop() +{ + alSourceStop(inst); + checkALError("stopping"); +} + +void OpenAL_Sound::pause() +{ + alSourcePause(inst); + checkALError("pausing"); +} + +bool OpenAL_Sound::isPlaying() +{ + ALint state; + alGetSourcei(inst, AL_SOURCE_STATE, &state); + + return state == AL_PLAYING; +} + +void OpenAL_Sound::setVolume(float volume) +{ + if(volume > 1.0) volume = 1.0; + if(volume < 0.0) volume = 0.0; + alSourcef(inst, AL_GAIN, volume); + checkALError("setting volume"); +} + +void OpenAL_Sound::setPos(float x, float y, float z) +{ + alSource3f(inst, AL_POSITION, x, y, z); + checkALError("setting position"); +} + + + + + + + + + + + + + + + + + + + + +Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) +{ + assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); + + if(stream) + return new OpenAL_Stream_Instance(source->getStream(), owner); + + // Load the buffer if it hasn't been done already + if(bufferID == 0) + { + // Get an input stream and load the file from it + InputStream *inp = source->getStream(); + + std::vector buffer; + + // Add 32 kb at each increment + const int ADD = 32*1024; + + // Fill the buffer. We increase the buffer until it's large + // enough to fit all the data. + while(true) + { + // Increase the buffer + int oldlen = buffer.size(); + buffer.resize(oldlen+ADD); + + // Read the data + size_t len = inp->getData(&buffer[oldlen], ADD); + + // If we read less than requested, we're done. + if(len < ADD) + { + // Downsize the buffer to the right size + buffer.resize(oldlen+len); + break; + } + } + + // Get the format + int fmt, rate; + getALFormat(inp, fmt, rate); + + // We don't need the file anymore + inp->drop(); + source->drop(); + source = NULL; + + // Move the data into OpenAL + alGenBuffers(1, &bufferID); + alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); + checkALError("loading sound buffer"); + } // End of buffer loading + + // At this point, the file data has been loaded into the buffer + // in 'bufferID', and we should be ready to go. + assert(bufferID != 0); + + return new OpenAL_Simple_Instance(bufferID); +} + + +// ---- OpenAL_Simple_Instance ---- + +OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) +{ + // Create instance and associate buffer + alGenSources(1, &inst); + alSourcei(inst, AL_BUFFER, buf); +} + +OpenAL_Simple_Instance::~OpenAL_Simple_Instance() +{ + // Stop + alSourceStop(inst); + + // Return sound + alDeleteSources(1, &inst); +} + + +// ---- OpenAL_Stream_Instance ---- + +OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, + OpenAL_Manager *_owner) + : stream(_stream), owner(_owner) +{ + // Deduce the file format from the stream info + getALFormat(stream, fmt, rate); + + // Create the buffers and the sound instance + alGenBuffers(BUFS, bufs); + alGenSources(1, &inst); + + checkALError("initializing"); + + // Fill the buffers and que them + for(int i=0; iadd_stream(this); +} + +void OpenAL_Stream_Instance::queueBuffer(ALuint bId) +{ + char buf[SIZE]; + + // Get the data + int len = stream->getData(buf, SIZE); + if(len == 0) + return; + + // .. and stash it + alBufferData(bId, fmt, buf, len, rate); + alSourceQueueBuffers(inst, 1, &bId); +} + +OpenAL_Stream_Instance::~OpenAL_Stream_Instance() +{ + // Remove ourselves from streaming list + owner->remove_stream(lit); + + // Stop + alSourceStop(inst); + + // Kill the input stream + stream->drop(); + + // Return sound + alDeleteSources(1, &inst); + + // Delete buffers + alDeleteBuffers(BUFS, bufs); +} + +void OpenAL_Stream_Instance::update() +{ + ALint count; + alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); + + for(int i = 0;i < count;i++) + { + // Unque a finished buffer + ALuint bId; + alSourceUnqueueBuffers(inst, 1, &bId); + + // Queue a new buffer + queueBuffer(bId); + } +} diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h new file mode 100644 index 000000000..95d27845c --- /dev/null +++ b/sound/outputs/openal_out.h @@ -0,0 +1,109 @@ +#ifndef MANGLE_SOUND_OPENAL_OUT_H +#define MANGLE_SOUND_OPENAL_OUT_H + +#include "../output.h" + +#include +#include +#include + +namespace Mangle { +namespace Sound { + +/// OpenAL sound output +class OpenAL_Sound : public Sound +{ + protected: + ALuint inst; + + public: + OpenAL_Sound(SampleSource *input); + ~OpenAL_Sound(); + + /// Play or resume the sound + void play(); + + /// Stop the sound + void stop(); + + /// Pause the sound, may be resumed later + void pause(); + + /// Check if the sound is still playing + bool isPlaying(); + + /// Set the volume. The parameter must be between 0.0 and 1.0. + void setVolume(float); + + /// Set the 3D position. + void setPos(float x, float y, float z); +}; + +class OpenALFactory : public SoundFactory +{ + ALCdevice *Device; + ALCcontext *Context; + bool didSetup; + + public: + /// Initialize object. Pass true (default) if you want the + /// constructor to set up the current ALCdevice and ALCcontext for + /// you. + OpenALFactory(bool doSetup = true) + : didSetup(doSetup) + { + needsUpdate = false; + has3D = true; + canRepeatStream = false; + canLoadFile = false; + canLoadStream = false; + canLoadSource = true; + + if(doSetup) + { + // Set up sound system + Device = alcOpenDevice(NULL); + Context = alcCreateContext(Device, NULL); + + if(!Device || !Context) + fail("Failed to initialize context or device"); + + alcMakeContextCurrent(Context); + } + } + + ~OpenALFactory() + { + // Deinitialize sound system + if(didSetup) + { + alcMakeContextCurrent(NULL); + if(Context) alcDestroyContext(Context); + if(Device) alcCloseDevice(Device); + } + } + + Sound *load(const std::string &file, bool stream=false) { assert(0); } + Sound *load(Stream::Stream *input, bool stream=false) { assert(0); } + Sound *load(SampleSource* input, bool stream=false) + { return new OpenAL_Sound(input); } + + void update() {} + setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { + ALfloat orient[6]; + orient[0] = fx; + orient[1] = fy; + orient[2] = fz; + orient[3] = ux; + orient[4] = uy; + orient[5] = uz; + alListener3f(AL_POSITION, x, y, z); + alListenerfv(AL_ORIENTATION, orient); + } +}; + +}} // namespaces +#endif From 0c18c4db039e6c92f43049ec259dea87c84584bc Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 16:16:33 +0100 Subject: [PATCH 022/111] Added memory_stream.h, rewrote tests --- stream/servers/memory_stream.h | 60 +++++++++++++++++++ stream/tests/Makefile | 6 +- stream/tests/audiere_client_test.cpp | 5 +- stream/tests/dummy_input.cpp | 48 --------------- .../tests/{dummy_test.cpp => memory_test.cpp} | 7 ++- stream/tests/ogre_client_test.cpp | 5 +- 6 files changed, 73 insertions(+), 58 deletions(-) create mode 100644 stream/servers/memory_stream.h delete mode 100644 stream/tests/dummy_input.cpp rename stream/tests/{dummy_test.cpp => memory_test.cpp} (89%) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h new file mode 100644 index 000000000..063b0fcf1 --- /dev/null +++ b/stream/servers/memory_stream.h @@ -0,0 +1,60 @@ +#ifndef MANGLE_STREAM_MEMSERVER_H +#define MANGLE_STREAM_MEMSERVER_H + +#include +#include "../stream.h" + +namespace Mangle { +namespace Stream { + +/** A Stream wrapping a memory buffer + + This will create a fully seekable stream out any pointer/length + pair you give it. + */ +class MemoryStream : public Stream +{ + const void *data; + size_t length, pos; + + public: + MemoryStream(const void *ptr, size_t len) + : data(ptr), length(len), pos(0) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + size_t read(void *buf, size_t count) + { + assert(pos <= length); + + // Don't read more than we have + if(count > (length - pos)) + count = length - pos; + + // Copy data + if(count) + memcpy(buf, ((char*)data)+pos, count); + + // aaand remember to increase the count + pos += count; + + return count; + } + + void seek(size_t _pos) + { + pos = _pos; + if(pos > length) + pos = length; + } + + size_t tell() const { return pos; } + size_t size() const { return length; } + bool eof() const { return pos == length; } +}; + +}} // namespaces +#endif diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 84ec228cf..8006572bd 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -6,13 +6,13 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_AUDIERE=-laudiere -ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../stream.h ../clients/iwrapper.h ../clients/ogre_datastream.h +ogre_client_test: ogre_client_test.cpp ../stream.h ../clients/iwrapper.h ../clients/ogre_datastream.h $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -audiere_client_test: audiere_client_test.cpp dummy_input.cpp ../stream.h ../clients/iwrapper.h ../clients/audiere_file.h ../clients/audiere_file.cpp +audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/iwrapper.h ../clients/audiere_file.h ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -dummy_test: dummy_test.cpp dummy_input.cpp ../stream.h +memory_test: memory_test.cpp ../stream.h ../servers/memory_stream.h $(GCC) $< -o $@ clean: diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp index f64de0649..01f548714 100644 --- a/stream/tests/audiere_client_test.cpp +++ b/stream/tests/audiere_client_test.cpp @@ -1,8 +1,9 @@ -#include "dummy_input.cpp" +#include "../servers/memory_stream.h" #include "../clients/audiere_file.h" #include #include +using namespace Mangle::Stream; using namespace audiere; using namespace std; @@ -10,7 +11,7 @@ int main() { char str[12]; memset(str, 0, 12); - Stream *inp = new DummyInput(); + Stream *inp = new MemoryStream("hello world", 11); FilePtr p(new AudiereFile(inp, true)); cout << "pos=" << p->tell() << endl; p->read(str, 2); diff --git a/stream/tests/dummy_input.cpp b/stream/tests/dummy_input.cpp deleted file mode 100644 index c93f42160..000000000 --- a/stream/tests/dummy_input.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// This file is shared between several test programs -#include "../stream.h" -#include -#include - -using namespace Mangle::Stream; - -// A simple dummy stream -const char _data[12] = "hello world"; - -class DummyInput : public Stream -{ -private: - int pos; - -public: - DummyInput() : pos(0) - { - isSeekable = true; - hasPosition = true; - hasSize = true; - } - - size_t read(void *buf, size_t count) - { - assert(pos >= 0 && pos <= 11); - if(count+pos > 11) - count = 11-pos; - assert(count <= 11); - - memcpy(buf, _data+pos, count); - pos += count; - - assert(pos >= 0 && pos <= 11); - return count; - } - - void seek(size_t npos) - { - if(npos > 11) npos = 11; - pos = npos; - } - - size_t tell() const { return pos; } - size_t size() const { return 11; } - - bool eof() const { return pos == 11; } -}; diff --git a/stream/tests/dummy_test.cpp b/stream/tests/memory_test.cpp similarity index 89% rename from stream/tests/dummy_test.cpp rename to stream/tests/memory_test.cpp index cd8f64e3c..7e7f34075 100644 --- a/stream/tests/dummy_test.cpp +++ b/stream/tests/memory_test.cpp @@ -1,13 +1,14 @@ -#include "dummy_input.cpp" - #include #include +#include "../servers/memory_stream.h" + +using namespace Mangle::Stream; using namespace std; int main() { - Stream *inp = new DummyInput(); + Stream *inp = new MemoryStream("hello world", 11); cout << "Size: " << inp->size() << endl; cout << "Pos: " << inp->tell() << "\nSeeking...\n"; diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp index a2bae1c8e..530113362 100644 --- a/stream/tests/ogre_client_test.cpp +++ b/stream/tests/ogre_client_test.cpp @@ -1,13 +1,14 @@ -#include "dummy_input.cpp" +#include "../servers/memory_stream.h" #include "ogre_datastream.h" #include +using namespace Mangle::Stream; using namespace Ogre; using namespace std; int main() { - Stream *inp = new DummyInput(); + Stream *inp = new MemoryStream("hello world", 11); DataStreamPtr p(new MangleDataStream("hello", inp, true)); cout << "Name: " << p->getName() << endl; cout << "As string: " << p->getAsString() << endl; From 084fd9dfb2557409072481991e1e2f2b2924742a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 16:23:09 +0100 Subject: [PATCH 023/111] Added getPtr and clone to MemoryStream --- stream/servers/memory_stream.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 063b0fcf1..5bceb6bbd 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -54,6 +54,25 @@ class MemoryStream : public Stream size_t tell() const { return pos; } size_t size() const { return length; } bool eof() const { return pos == length; } + + // New members in MemoryStream: + + /// Get the base pointer to the entire buffer + const void *getPtr() const { return data; } + + /// Clone this memory stream + /** Make a new stream of the same buffer. If setPos is true, we also + set the clone's position to be the same as ours. + + No memory is copied during this operation, the new stream is + just another 'view' into the same shared memory buffer. + */ + MemoryStream *clone(bool setPos=false) const + { + MemoryStream *res = new MemoryStream(data, length); + if(setPos) res->seek(pos); + return res; + } }; }} // namespaces From 9cb57f9ccd5a2f0be829224fb057f86b1e9db981 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 16:40:02 +0100 Subject: [PATCH 024/111] Added boost/shared_ptr to memory_stream for future testing --- stream/servers/memory_stream.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 5bceb6bbd..f6e878dd1 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -3,10 +3,16 @@ #include #include "../stream.h" +#include namespace Mangle { namespace Stream { +// Do this before the class declaration, since the class itself +// depends on it +class MemoryStream; +typedef boost::shared_ptr MemoryStreamPtr; + /** A Stream wrapping a memory buffer This will create a fully seekable stream out any pointer/length @@ -67,9 +73,9 @@ class MemoryStream : public Stream No memory is copied during this operation, the new stream is just another 'view' into the same shared memory buffer. */ - MemoryStream *clone(bool setPos=false) const + MemoryStreamPtr clone(bool setPos=false) const { - MemoryStream *res = new MemoryStream(data, length); + MemoryStreamPtr res = new MemoryStream(data, length); if(setPos) res->seek(pos); return res; } From 71c3be13fcdc4aebf3cfa03803966da3aca07c68 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 16:52:47 +0100 Subject: [PATCH 025/111] Started buffer_stream.h, WIP --- stream/filters/buffer_stream.h | 23 +++++++++++++++++++++++ stream/servers/memory_stream.h | 17 +++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 stream/filters/buffer_stream.h diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.h new file mode 100644 index 000000000..f8290ea5e --- /dev/null +++ b/stream/filters/buffer_stream.h @@ -0,0 +1,23 @@ +#ifndef MANGLE_STREAM_BUFFER_H +#define MANGLE_STREAM_BUFFER_H + +#include "../servers/memory_stream.h" +#include "../clients/iwrapper.h" + +namespace Mangle { +namespace Stream { + +/** A Stream that reads another Stream into a buffer, and serves it as + a MemoryStream. + */ +class BufferStream : public MemoryStream +{ + public: + BufferStream(Stream *input) + { + // Allocate memory, read the stream into it. Then call set() + } +}; + +}} // namespaces +#endif diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index f6e878dd1..c47ffa535 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -32,8 +32,17 @@ class MemoryStream : public Stream hasSize = true; } + MemoryStream() + : data(NULL), length(0), pos(0); + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + size_t read(void *buf, size_t count) { + assert(data != NULL); assert(pos <= length); // Don't read more than we have @@ -63,6 +72,14 @@ class MemoryStream : public Stream // New members in MemoryStream: + /// Set a new buffer and length. This will rewind the position to zero. + void set(const void* _data, size_t _length) + { + data = _data; + length = _length; + pos = 0; + } + /// Get the base pointer to the entire buffer const void *getPtr() const { return data; } From aafe01dcccc1d0dac958585e56a11e42c7a29721 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Dec 2009 11:50:21 +0100 Subject: [PATCH 026/111] Finished memory and buffer streams (for now) --- stream/filters/buffer_stream.h | 47 ++++++++++++++++++++++++++++++++-- stream/servers/memory_stream.h | 15 ++++++----- stream/tests/Makefile | 3 +++ stream/tests/buffer_test.cpp | 41 +++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 stream/tests/buffer_test.cpp diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.h index f8290ea5e..7790a7542 100644 --- a/stream/filters/buffer_stream.h +++ b/stream/filters/buffer_stream.h @@ -2,20 +2,63 @@ #define MANGLE_STREAM_BUFFER_H #include "../servers/memory_stream.h" -#include "../clients/iwrapper.h" +#include namespace Mangle { namespace Stream { /** A Stream that reads another Stream into a buffer, and serves it as - a MemoryStream. + a MemoryStream. Might be expanded with other capabilities later. */ class BufferStream : public MemoryStream { + std::vector buffer; + public: BufferStream(Stream *input) { // Allocate memory, read the stream into it. Then call set() + if(input->hasSize) + { + // We assume that we can get the position as well + assert(input->hasPosition); + + // Calculate how much is left of the stream + size_t left = input->size() - input->tell(); + + // Allocate the buffer and fill it + buffer.resize(left); + input->read(&buffer[0], left); + } + else + { + // We DON'T know how big the stream is. We'll have to read + // it in increments. + const int ADD = 32*1024; + size_t len=0, newlen; + + while(!input->eof()) + { + // Read one block + newlen = len + ADD; + buffer.resize(newlen); + size_t read = input->read(&buffer[len], ADD); + + // Increase the total length + len += read; + + // If we read less than expected, we should be at the + // end of the stream + assert(read == ADD || input->eof()); + } + + // Downsize to match the real length + buffer.resize(len); + } + + // After the buffer has been filled, set up our MemoryStream + // ancestor to reference it. + set(&buffer[0], buffer.size()); } }; diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index c47ffa535..03f7a42f2 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -3,15 +3,14 @@ #include #include "../stream.h" -#include namespace Mangle { namespace Stream { // Do this before the class declaration, since the class itself -// depends on it -class MemoryStream; -typedef boost::shared_ptr MemoryStreamPtr; +// depends on it. TODO: Postponed for later +//class MemoryStream; +//typedef boost::shared_ptr MemoryStreamPtr; /** A Stream wrapping a memory buffer @@ -33,7 +32,7 @@ class MemoryStream : public Stream } MemoryStream() - : data(NULL), length(0), pos(0); + : data(NULL), length(0), pos(0) { isSeekable = true; hasPosition = true; @@ -89,10 +88,12 @@ class MemoryStream : public Stream No memory is copied during this operation, the new stream is just another 'view' into the same shared memory buffer. + + TODO: Rewrite to use smart pointers */ - MemoryStreamPtr clone(bool setPos=false) const + MemoryStream* clone(bool setPos=false) const { - MemoryStreamPtr res = new MemoryStream(data, length); + MemoryStream* res = new MemoryStream(data, length); if(setPos) res->seek(pos); return res; } diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 8006572bd..c86a7397f 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -15,5 +15,8 @@ audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/iwrapper.h . memory_test: memory_test.cpp ../stream.h ../servers/memory_stream.h $(GCC) $< -o $@ +buffer_test: buffer_test.cpp ../stream.h ../servers/memory_stream.h ../filters/buffer_stream.h + $(GCC) $< -o $@ + clean: rm *_test diff --git a/stream/tests/buffer_test.cpp b/stream/tests/buffer_test.cpp new file mode 100644 index 000000000..24bb7e070 --- /dev/null +++ b/stream/tests/buffer_test.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include "../filters/buffer_stream.h" + +using namespace Mangle::Stream; +using namespace std; + +int main() +{ + Stream *orig = new MemoryStream("hello world", 11); + Stream *inp = new BufferStream(orig); + + cout << "Size: " << inp->size() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking...\n"; + inp->seek(3); + cout << "Pos: " << inp->tell() << endl; + char data[12]; + memset(data, 0, 12); + cout << "Reading: " << inp->read(data, 4) << endl; + cout << "Four bytes: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking again...\n"; + inp->seek(33); + cout << "Pos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << "\nSeek to 6\n"; + inp->seek(6); + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + cout << "Over-reading: " << inp->read(data, 200) << endl; + cout << "Result: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + inp->seek(0); + cout << "Finally, reading the entire string: " << inp->read(data,11) << endl; + cout << "Result: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + + return 0; +} From d60f0fa9009992e2ae2a9cee1f11107591daa7ea Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Dec 2009 12:29:24 +0100 Subject: [PATCH 027/111] Finished first rewrite draft. Not done or tested, but good enough for now. --- sound/{outputs => filters}/input_filter.h | 32 ++-- sound/{outputs => filters}/openal_audiere.h | 14 +- sound/outputs/openal_out.cpp | 175 ++------------------ sound/outputs/openal_out.h | 8 +- sound/tests/audiere_test.cpp | 7 - sound/tests/ffmpeg_openal_test.cpp | 7 - sound/tests/openal_audiere_test.cpp | 4 +- 7 files changed, 39 insertions(+), 208 deletions(-) rename sound/{outputs => filters}/input_filter.h (67%) rename sound/{outputs => filters}/openal_audiere.h (59%) delete mode 100644 sound/tests/audiere_test.cpp delete mode 100644 sound/tests/ffmpeg_openal_test.cpp diff --git a/sound/outputs/input_filter.h b/sound/filters/input_filter.h similarity index 67% rename from sound/outputs/input_filter.h rename to sound/filters/input_filter.h index 455a60e14..85a97321d 100644 --- a/sound/outputs/input_filter.h +++ b/sound/filters/input_filter.h @@ -1,7 +1,7 @@ #ifndef MANGLE_INPUT_FILTER_H #define MANGLE_INPUT_FILTER_H -#include "../sound.h" +#include "../output.h" #include @@ -10,39 +10,29 @@ namespace Sound { /** @brief This filter class adds file loading capabilities to a - Sound::Manager class, by associating an InputManager with it. + Sound::SoundFactory class, by associating an SampleSourceLoader + with it. - The class takes an existing Manager able to load streams, and - associates an InputManager with it. The combined class is able to - load files directly. - - Example: - \code - - // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot - // decode sound files on its own. - InputFilter mg(new OpenAL_Manager, new FFM_InputManager); - - // We can now load filenames directly. - mg.load("file1.mp3"); - \endcode + The class takes an existing SoundFactory able to load streams, and + associates an SampleSourceLoader with it. The combined class is + able to load files directly. */ -class InputFilter : public Manager +class InputFilter : public SoundFactory { protected: - Manager *snd; - InputManager *inp; + SoundFactory *snd; + SampleSourceLoader *inp; public: /// Empty constructor InputFilter() {} /// Assign an input manager and a sound manager to this object - InputFilter(Manager *_snd, InputManager *_inp) + InputFilter(SoundFactory *_snd, SampleSourceLoader *_inp) { set(_snd, _inp); } /// Assign an input manager and a sound manager to this object - void set(Manager *_snd, InputManager *_inp) + void set(SoundFactory *_snd, SampleSourceLoader *_inp) { inp = _inp; snd = _snd; diff --git a/sound/outputs/openal_audiere.h b/sound/filters/openal_audiere.h similarity index 59% rename from sound/outputs/openal_audiere.h rename to sound/filters/openal_audiere.h index 65947b22f..45024094a 100644 --- a/sound/outputs/openal_audiere.h +++ b/sound/filters/openal_audiere.h @@ -2,23 +2,23 @@ #define MANGLE_FFMPEG_OPENAL_H #include "input_filter.h" -#include "input_audiere.h" -#include "output_openal.h" +#include "../sources/audiere_source.h" +#include "../outputs/openal_out.h" namespace Mangle { namespace Sound { /// A InputFilter that adds audiere decoding to OpenAL. Audiere has /// it's own output, but OpenAL sports 3D and other advanced features. -class OpenAL_Audiere_Manager : public InputFilter +class OpenAL_Audiere_Factory : public InputFilter { public: - OpenAL_Audiere_Manager() + OpenAL_Audiere_Factory() { - set(new OpenAL_Manager, - new AudiereInput); + set(new OpenAL_Factory, + new AudiereLoader); } - ~OpenAL_Audiere_Manager() + ~OpenAL_Audiere_Factory() { delete snd; delete inp; diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 5d3633e75..b5ec9c171 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,8 +1,6 @@ #include "openal_out.h" #include -// ALuint bufferID; - using namespace Mangle::Sound; // ---- Helper functions and classes ---- @@ -65,11 +63,6 @@ static void getALFormat(InputStream *inp, int &fmt, int &rate) // ---- OpenAL_Sound ---- -OpenAL_Sound::OpenAL_Sound(SampleSource *input) -{ - -} - void OpenAL_Sound::play() { alSourcePlay(inst); @@ -110,174 +103,34 @@ void OpenAL_Sound::setPos(float x, float y, float z) checkALError("setting position"); } - - - - - - - - - - - - - - - - - - - -Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) +OpenAL_Sound::OpenAL_Sound(SampleSource *input) { - assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); - - if(stream) - return new OpenAL_Stream_Instance(source->getStream(), owner); - - // Load the buffer if it hasn't been done already - if(bufferID == 0) - { - // Get an input stream and load the file from it - InputStream *inp = source->getStream(); - - std::vector buffer; + // Get the format + int fmt, rate; + getALFormat(inp, fmt, rate); - // Add 32 kb at each increment - const int ADD = 32*1024; + // Read the entire stream into a buffer + BufferStream buf(input); - // Fill the buffer. We increase the buffer until it's large - // enough to fit all the data. - while(true) - { - // Increase the buffer - int oldlen = buffer.size(); - buffer.resize(oldlen+ADD); - - // Read the data - size_t len = inp->getData(&buffer[oldlen], ADD); - - // If we read less than requested, we're done. - if(len < ADD) - { - // Downsize the buffer to the right size - buffer.resize(oldlen+len); - break; - } - } - - // Get the format - int fmt, rate; - getALFormat(inp, fmt, rate); - - // We don't need the file anymore - inp->drop(); - source->drop(); - source = NULL; - - // Move the data into OpenAL - alGenBuffers(1, &bufferID); - alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); - checkALError("loading sound buffer"); - } // End of buffer loading - - // At this point, the file data has been loaded into the buffer - // in 'bufferID', and we should be ready to go. + // Move the data into OpenAL + alGenBuffers(1, &bufferID); assert(bufferID != 0); + alBufferData(bufferID, fmt, &buf.getPtr(), buf.size(), rate); + checkALError("loading sound buffer"); - return new OpenAL_Simple_Instance(bufferID); -} - - -// ---- OpenAL_Simple_Instance ---- - -OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) -{ - // Create instance and associate buffer + // Create a source alGenSources(1, &inst); alSourcei(inst, AL_BUFFER, buf); } -OpenAL_Simple_Instance::~OpenAL_Simple_Instance() +OpenAL_Sound::~OpenAL_Sound() { // Stop alSourceStop(inst); // Return sound alDeleteSources(1, &inst); -} - - -// ---- OpenAL_Stream_Instance ---- - -OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, - OpenAL_Manager *_owner) - : stream(_stream), owner(_owner) -{ - // Deduce the file format from the stream info - getALFormat(stream, fmt, rate); - - // Create the buffers and the sound instance - alGenBuffers(BUFS, bufs); - alGenSources(1, &inst); - - checkALError("initializing"); - - // Fill the buffers and que them - for(int i=0; iadd_stream(this); -} - -void OpenAL_Stream_Instance::queueBuffer(ALuint bId) -{ - char buf[SIZE]; - - // Get the data - int len = stream->getData(buf, SIZE); - if(len == 0) - return; - // .. and stash it - alBufferData(bId, fmt, buf, len, rate); - alSourceQueueBuffers(inst, 1, &bId); -} - -OpenAL_Stream_Instance::~OpenAL_Stream_Instance() -{ - // Remove ourselves from streaming list - owner->remove_stream(lit); - - // Stop - alSourceStop(inst); - - // Kill the input stream - stream->drop(); - - // Return sound - alDeleteSources(1, &inst); - - // Delete buffers - alDeleteBuffers(BUFS, bufs); -} - -void OpenAL_Stream_Instance::update() -{ - ALint count; - alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); - - for(int i = 0;i < count;i++) - { - // Unque a finished buffer - ALuint bId; - alSourceUnqueueBuffers(inst, 1, &bId); - - // Queue a new buffer - queueBuffer(bId); - } + // Delete buffer + alDeleteBuffers(1, &bufferID); } diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index 95d27845c..1cce21607 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -2,6 +2,7 @@ #define MANGLE_SOUND_OPENAL_OUT_H #include "../output.h" +#include "../../stream/filters/buffer_stream.h" #include #include @@ -15,6 +16,7 @@ class OpenAL_Sound : public Sound { protected: ALuint inst; + ALuint bufferID; public: OpenAL_Sound(SampleSource *input); @@ -39,7 +41,7 @@ class OpenAL_Sound : public Sound void setPos(float x, float y, float z); }; -class OpenALFactory : public SoundFactory +class OpenAL_Factory : public SoundFactory { ALCdevice *Device; ALCcontext *Context; @@ -49,7 +51,7 @@ class OpenALFactory : public SoundFactory /// Initialize object. Pass true (default) if you want the /// constructor to set up the current ALCdevice and ALCcontext for /// you. - OpenALFactory(bool doSetup = true) + OpenAL_Factory(bool doSetup = true) : didSetup(doSetup) { needsUpdate = false; @@ -72,7 +74,7 @@ class OpenALFactory : public SoundFactory } } - ~OpenALFactory() + ~OpenAL_Factory() { // Deinitialize sound system if(didSetup) diff --git a/sound/tests/audiere_test.cpp b/sound/tests/audiere_test.cpp deleted file mode 100644 index defb8c95f..000000000 --- a/sound/tests/audiere_test.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "audiere_imp.h" - -using namespace Mangle::Sound; - -AudiereManager mg; - -#include "common.cpp" diff --git a/sound/tests/ffmpeg_openal_test.cpp b/sound/tests/ffmpeg_openal_test.cpp deleted file mode 100644 index 28391af3e..000000000 --- a/sound/tests/ffmpeg_openal_test.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "openal_ffmpeg.h" - -using namespace Mangle::Sound; - -OpenAL_FFM_Manager mg; - -#include "common.cpp" diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp index c9011f48e..3036e02a2 100644 --- a/sound/tests/openal_audiere_test.cpp +++ b/sound/tests/openal_audiere_test.cpp @@ -1,7 +1,7 @@ -#include "openal_audiere.h" +#include "../filters/openal_audiere.h" using namespace Mangle::Sound; -OpenAL_Audiere_Manager mg; +OpenAL_Audiere_Factory mg; #include "common.cpp" From d22bea5eab9e8064738aab4456952bda3bb88f95 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Dec 2009 12:36:35 +0100 Subject: [PATCH 028/111] Added more capabilities to Audiere sources --- sound/source.h | 3 ++- sound/sources/audiere_source.cpp | 25 ++++++++++++++++--------- sound/sources/audiere_source.h | 4 ++++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/sound/source.h b/sound/source.h index 7361fb118..db5bc6b47 100644 --- a/sound/source.h +++ b/sound/source.h @@ -33,7 +33,8 @@ class SampleSource : public Stream::Stream bool eof() const { return isEof; } - // Disabled functions + // Disabled functions by default. You can still override them in + // subclasses. void seek(size_t pos) const { assert(0); } size_t tell() const { assert(0); } size_t size() const { assert(0); } diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 6ebc4f881..d99c6c4ee 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -28,11 +28,14 @@ void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) { SampleFormat fmt; sample->getFormat(*channels, *rate, fmt); - if(fmt == SF_U8) - *bits = 8; - else if(fmt == SF_S16) - *bits = 16; - else assert(0); + if(bits) + { + if(fmt == SF_U8) + *bits = 8; + else if(fmt == SF_S16) + *bits = 16; + else assert(0); + } } /* @@ -103,7 +106,7 @@ AudiereSource::AudiereSource(const std::string &file) if(!sample) fail("Couldn't load file " + file); - getFormat(); + setup(); } AudiereSource::AudiereSource(Stream::Stream *input) @@ -114,15 +117,15 @@ AudiereSource::AudiereSource(Stream::Stream *input) if(!sample) fail("Couldn't load stream"); - getFormat(); + setup(); } AudiereSource::AudiereSource(audiere::SampleSourcePtr src) : sample(src) -{ assert(sample); getFormat(); } +{ assert(sample); setup(); } // Common function called from all constructors -AudiereSource::getFormat() +AudiereSource::setup() { assert(sample); @@ -136,4 +139,8 @@ AudiereSource::getFormat() // Make sure that our pullover hack will work. Increase this size if // this doesn't work in all cases. assert(frameSize <= PSIZE); + + isSeekable = sample->isSeekable(); + hasPosition = true; + hasSize = true; } diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h index 12ae81bce..1348e112c 100644 --- a/sound/sources/audiere_source.h +++ b/sound/sources/audiere_source.h @@ -40,6 +40,10 @@ class AudiereSource : public SampleSource void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); size_t read(void *data, size_t length); + + void seek(size_t pos) const { sample->setPosition(pos); } + size_t tell() const { return sample->getPosition(); } + size_t size() const { return sample->getLength(); } }; #include "loadertemplate.h" From 7d36b599d0a991857b1d5952f9831b0828fad013 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Dec 2009 16:15:46 +0100 Subject: [PATCH 029/111] Added Sound.clone(), implemented with OpenAL. --- sound/output.h | 40 ++++++++++++++++++++++++++---------- sound/outputs/openal_out.cpp | 38 +++++++++++++++++++++++++++++++--- sound/outputs/openal_out.h | 24 ++++++++++------------ 3 files changed, 75 insertions(+), 27 deletions(-) diff --git a/sound/output.h b/sound/output.h index 4cdac7bed..0829e4789 100644 --- a/sound/output.h +++ b/sound/output.h @@ -17,6 +17,11 @@ namespace Sound { may be connected to a SampleSource or read directly from a file, and they may support 3d sounds, looping and other features depending on the capabilities of the backend system. + + To create multiple instances of one sound, it is recommended to + 'clone' an existing instance instead of reloading it from + file. Cloned sounds will often (depending on the back-end) use + less memory due to shared buffers. */ class Sound { @@ -31,14 +36,33 @@ class Sound virtual void pause() = 0; /// Check if the sound is still playing - virtual bool isPlaying() = 0; + virtual bool isPlaying() = 0 const; /// Set the volume. The parameter must be between 0.0 and 1.0. virtual void setVolume(float) = 0; - /// Set the position. May not have any effect on 2D sounds. + /// Set left/right pan. -1.0 is left, 0.0 is center and 1.0 is right. + virtual void setPan(float) = 0; + + /// Set the position. May not work with all backends. virtual void setPos(float x, float y, float z) = 0; + /// Set loop mode + virtual void setRepeat(bool) = 0; + + /// Set streaming mode. + /** This may be used by implementations to optimize for very large + files. If streaming mode is off (default), most implementations + will load the entire file into memory before starting playback. + */ + virtual void setStreaming(bool) = 0; + + /// Create a new instance of this sound. + /** Playback status is not cloned, only the sound data + itself. Back-ends can use this as a means of sharing data and + saving memory. */ + virtual Sound* clone() const = 0; + /// Virtual destructor virtual ~Sound() {} }; @@ -71,12 +95,6 @@ class SoundFactory */ bool has3D; - /** @brief true if 'repeat' and 'stream' can be used simultaneously. - If false, repeating a streamed sound will give undefined - behavior. - */ - bool canRepeatStream; - /// true if we can load sounds directly from file (containing encoded data) bool canLoadFile; @@ -99,7 +117,7 @@ class SoundFactory large files, but they are not required to. @return a new Sound object */ - virtual Sound *load(SampleSource *input, bool stream=false) = 0; + virtual Sound *load(SampleSource *input) = 0; /** @brief Load a sound file from stream. Only valid if canLoadStream @@ -109,7 +127,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(Stream::Stream *input, bool stream=false) = 0; + virtual Sound *load(Stream::Stream *input) = 0; /** @brief Load a sound directly from file. Only valid if canLoadFile @@ -119,7 +137,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(const std::string &file, bool stream=false) = 0; + virtual Sound *load(const std::string &file) = 0; /// Call this every frame if needsUpdate is true /** diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index b5ec9c171..649a56aef 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -103,6 +103,28 @@ void OpenAL_Sound::setPos(float x, float y, float z) checkALError("setting position"); } +void OpenAL_Sound::setRepeat(bool rep) +{ + alSourcei(Source, AL_LOOPING, rep?AL_TRUE:AL_FALSE); +} + +void OpenAL_Sound::setup() +{ +} + +// Constructor used for cloned sounds +OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) + : refCnt(ref), bufferID(buf) +{ + // Increase the reference count + assert(ref != NULL); + *refCnt++; + + // Create a source + alGenSources(1, &inst); + alSourcei(inst, AL_BUFFER, bufferID); +} + OpenAL_Sound::OpenAL_Sound(SampleSource *input) { // Get the format @@ -120,7 +142,11 @@ OpenAL_Sound::OpenAL_Sound(SampleSource *input) // Create a source alGenSources(1, &inst); - alSourcei(inst, AL_BUFFER, buf); + alSourcei(inst, AL_BUFFER, bufferID); + + // Create a cheap reference counter for the buffer + refCnt = new int; + *refCnt = 1; } OpenAL_Sound::~OpenAL_Sound() @@ -131,6 +157,12 @@ OpenAL_Sound::~OpenAL_Sound() // Return sound alDeleteSources(1, &inst); - // Delete buffer - alDeleteBuffers(1, &bufferID); + // Decrease the reference + if(--(*refCnt)) + { + // We're the last owner. Delete the buffer and the counter + // itself. + alDeleteBuffers(1, &bufferID); + delete refCnt; + } } diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index 1cce21607..a0b8ed412 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -18,27 +18,26 @@ class OpenAL_Sound : public Sound ALuint inst; ALuint bufferID; + // Poor mans reference counting. Might improve this later. + int *refCnt; + public: OpenAL_Sound(SampleSource *input); + OpenAL_Sound(ALuint buf, int *ref); // Used for cloning ~OpenAL_Sound(); - /// Play or resume the sound void play(); - - /// Stop the sound void stop(); - - /// Pause the sound, may be resumed later void pause(); - - /// Check if the sound is still playing - bool isPlaying(); - - /// Set the volume. The parameter must be between 0.0 and 1.0. + bool isPlaying() const; void setVolume(float); - - /// Set the 3D position. void setPos(float x, float y, float z); + void setRepeat(bool); + void setStreaming(bool) {} // Not implemented yet + Sound* clone() const; + + /// Not implemented + void setPan(float) {} }; class OpenAL_Factory : public SoundFactory @@ -56,7 +55,6 @@ class OpenAL_Factory : public SoundFactory { needsUpdate = false; has3D = true; - canRepeatStream = false; canLoadFile = false; canLoadStream = false; canLoadSource = true; From 674ac556cc51e42636084c0c8667a44707792115 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 31 Dec 2009 14:48:34 +0100 Subject: [PATCH 030/111] Added StdStream, FileStream and SliceStream --- stream/filters/slice_stream.h | 54 ++++++++++++++++++++++++++++++++++ stream/servers/file_stream.h | 26 +++++++++++++++++ stream/servers/std_stream.h | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 stream/filters/slice_stream.h create mode 100644 stream/servers/file_stream.h create mode 100644 stream/servers/std_stream.h diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h new file mode 100644 index 000000000..5bd47d978 --- /dev/null +++ b/stream/filters/slice_stream.h @@ -0,0 +1,54 @@ +#ifndef MANGLE_STREAM_SLICE_H +#define MANGLE_STREAM_SLICE_H + +#include "../stream.h" + +namespace Mangle { +namespace Stream { + +/** A Stream that represents a subset (called a slice) of another stream. + */ +class SliceStream : public Stream +{ + Stream *src; + size_t offset, length, pos; + + public: + SliceStream(Stream *_src, size_t _offset, size_t _length) + : src(_src), offset(_offset), length(_length), pos(0) + { + assert(src->hasSize); + assert(src->isSeekable); + + // Make sure we can actually fit inside the source stream + assert(src->size() <= offset+length); + } + + size_t read(void *buf, size_t count) + { + // Check that we're not reading past our slice + if(count > length-pos) + count = length-pos; + + // Seek into place and reading + src->seek(offset+pos); + count = src->read(buf, count); + + pos += count; + assert(pos <= length); + return count; + } + + void seek(size_t _pos) + { + pos = _pos; + if(pos > length) pos = length; + } + + bool eof() { return pos == length; } + size_t tell() { return pos; } + size_t size() { return length; } +}; + +}} // namespaces +#endif diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.h new file mode 100644 index 000000000..1d6f92032 --- /dev/null +++ b/stream/servers/file_stream.h @@ -0,0 +1,26 @@ +#ifndef MANGLE_STREAM_FILESERVER_H +#define MANGLE_STREAM_FILESERVER_H + +#include "std_stream.h" +#include + +namespace Mangle { +namespace Stream { + +/** Very simple file input stream, based on std::ifstream + */ +class FileStream : public StdStream +{ + std::ifstream file; + + public: + FileStream(const std::string &name) + : StdStream(&file) + { + file.open(name, ios::binary); + } + ~FileStream() { file.close(); } +}; + +}} // namespaces +#endif diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.h new file mode 100644 index 000000000..e93295f15 --- /dev/null +++ b/stream/servers/std_stream.h @@ -0,0 +1,55 @@ +#ifndef MANGLE_STREAM_FILESERVER_H +#define MANGLE_STREAM_FILESERVER_H + +#include "../stream.h" +#include + +namespace Mangle { +namespace Stream { + +/** Simplest wrapper for std::istream. + + TODO: No error checking yet. + */ +class StdStream : public Stream +{ + std::istream *inf; + + public: + StdStream(std::istream *_inf) + : inf(_inf) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + size_t read(void* buf, size_t len) + { + inf->read((char*)buf, len); + return inf->gcount(); + } + + void seek(size_t pos) + { inf->seekg(pos); } + + size_t tell() const + // Hack around the fact that ifstream->tellg() isn't const + { return ((StdStream*)this)->inf->tellg(); } + + size_t size() const + { + // Use the standard iostream size hack, terrible as it is. + std::streampos pos = inf->tellg(); + inf->seekg(0, ios_base::end); + size_t res = inf->tellg(); + inf->seekg(pos); + return res; + } + + bool eof() const + { return inf->eof(); } +}; + +}} // namespaces +#endif From c45170f4206189eb6124213083880013c03f3907 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 31 Dec 2009 15:37:01 +0100 Subject: [PATCH 031/111] Starting rewrite to boost::shared_ptr. Not done, not tested. --- sound/buffer.h | 57 -------------------------------- sound/filters/input_filter.h | 14 ++++---- sound/filters/openal_audiere.h | 5 --- sound/output.h | 13 +++++--- sound/source.h | 8 +++-- stream/clients/audiere_file.h | 9 ++--- stream/clients/iwrapper.h | 30 ----------------- stream/clients/ogre_datastream.h | 15 +++++---- stream/filters/buffer_stream.h | 4 ++- stream/filters/slice_stream.h | 6 ++-- stream/servers/file_stream.h | 2 ++ stream/servers/memory_stream.h | 12 +++---- stream/servers/ogre_datastream.h | 2 ++ stream/servers/phys_stream.h | 2 ++ stream/servers/std_stream.h | 2 ++ stream/stream.h | 3 ++ tools/shared_ptr.h | 3 ++ vfs/clients/ogre_archive.cpp | 16 ++++----- vfs/clients/ogre_archive.h | 13 ++++---- vfs/clients/wrapper.h | 30 ----------------- vfs/servers/ogre_vfs.cpp | 12 +++---- vfs/servers/ogre_vfs.h | 18 +++++----- vfs/servers/physfs_vfs.h | 20 +++++------ vfs/vfs.h | 25 ++++++++------ 24 files changed, 116 insertions(+), 205 deletions(-) delete mode 100644 sound/buffer.h delete mode 100644 stream/clients/iwrapper.h create mode 100644 tools/shared_ptr.h delete mode 100644 vfs/clients/wrapper.h diff --git a/sound/buffer.h b/sound/buffer.h deleted file mode 100644 index 9cb27bd9c..000000000 --- a/sound/buffer.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef MANGLE_SOUND_BUFFER_H -#define MANGLE_SOUND_BUFFER_H - -#include "source.h" -#include "sources/memsource.h" -#include -#include - -namespace Mangle { -namespace Sound { - -/** A sample buffer is a factory that creates SampleSources from one - single sound source. It is helpful when you have many instances of - one sound and want to use one shared memory buffer. - - This is just a helper class - you don't have to include it in your - program if you don't need it. -*/ -class SampleBuffer -{ - std::vector buffer; - - public: - /// Reads the source into a memory buffer. Not heavily optimized. - SampleBuffer(SampleSource *source) - { - size_t final = 0; - - while(!source->eof()) - { - const int add = 16*1024; - - // Allocate more memory - size_t newSize = final + add; - buffer.resize(newSize); - - // Fill in data - size_t read = source->read(&buffer[final], add); - - // If we couldn't read enough data, we should be at the end - // of the stream - assert(read == add || source->eof()); - - final += read; - } - - // Downsize the buffer to the actual length - buffer.resize(final); - } - - /// Get a new source - SampleSource *get() - { return new MemorySource(&buffer[0], buffer.size()); } -}; - -}} -#endif diff --git a/sound/filters/input_filter.h b/sound/filters/input_filter.h index 85a97321d..17dda5534 100644 --- a/sound/filters/input_filter.h +++ b/sound/filters/input_filter.h @@ -20,19 +20,19 @@ namespace Sound { class InputFilter : public SoundFactory { protected: - SoundFactory *snd; - SampleSourceLoader *inp; + SoundFactoryPtr snd; + SampleSourceLoaderPtr inp; public: /// Empty constructor InputFilter() {} /// Assign an input manager and a sound manager to this object - InputFilter(SoundFactory *_snd, SampleSourceLoader *_inp) + InputFilter(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp) { set(_snd, _inp); } /// Assign an input manager and a sound manager to this object - void set(SoundFactory *_snd, SampleSourceLoader *_inp) + void set(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp) { inp = _inp; snd = _snd; @@ -50,13 +50,13 @@ class InputFilter : public SoundFactory assert(canLoadSource && canLoadFile); } - virtual Sound *load(const std::string &file, bool stream=false) + virtual SoundPtr load(const std::string &file) { return load(inp->load(file), stream); } - virtual Sound *load(Stream::Stream *input, bool stream=false) + virtual SoundPtr load(Stream::StreamPtr input) { return load(inp->load(input), stream); } - virtual Sound *load(InputSource *input, bool stream=false) + virtual SoundPtr load(SampleSourcePtr input) { return snd->load(input, stream); } virtual void update() { snd->update(); } diff --git a/sound/filters/openal_audiere.h b/sound/filters/openal_audiere.h index 45024094a..9a2bfcf8c 100644 --- a/sound/filters/openal_audiere.h +++ b/sound/filters/openal_audiere.h @@ -18,11 +18,6 @@ class OpenAL_Audiere_Factory : public InputFilter set(new OpenAL_Factory, new AudiereLoader); } - ~OpenAL_Audiere_Factory() - { - delete snd; - delete inp; - } }; }} diff --git a/sound/output.h b/sound/output.h index 0829e4789..dfd190241 100644 --- a/sound/output.h +++ b/sound/output.h @@ -23,6 +23,9 @@ namespace Sound { file. Cloned sounds will often (depending on the back-end) use less memory due to shared buffers. */ +class Sound; +typedef boost::shared_ptr SoundPtr; + class Sound { public: @@ -61,7 +64,7 @@ class Sound /** Playback status is not cloned, only the sound data itself. Back-ends can use this as a means of sharing data and saving memory. */ - virtual Sound* clone() const = 0; + virtual SoundPtr clone() const = 0; /// Virtual destructor virtual ~Sound() {} @@ -117,7 +120,7 @@ class SoundFactory large files, but they are not required to. @return a new Sound object */ - virtual Sound *load(SampleSource *input) = 0; + virtual SoundPtr load(SampleSource *input) = 0; /** @brief Load a sound file from stream. Only valid if canLoadStream @@ -127,7 +130,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(Stream::Stream *input) = 0; + virtual SoundPtr load(Stream::Stream *input) = 0; /** @brief Load a sound directly from file. Only valid if canLoadFile @@ -137,7 +140,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(const std::string &file) = 0; + virtual SoundPtr load(const std::string &file) = 0; /// Call this every frame if needsUpdate is true /** @@ -161,6 +164,8 @@ class SoundFactory float ux, float uy, float uz) = 0; }; +typedef boost::shared_ptr SoundFactoryPtr; + }} // Namespaces #endif diff --git a/sound/source.h b/sound/source.h index db5bc6b47..0301f4bae 100644 --- a/sound/source.h +++ b/sound/source.h @@ -40,6 +40,8 @@ class SampleSource : public Stream::Stream size_t size() const { assert(0); } }; +typedef boost::shared_ptr SampleSourcePtr; + /// A factory interface for loading SampleSources from file or stream class SampleSourceLoader { @@ -51,14 +53,16 @@ class SampleSourceLoader bool canLoadFile; /// Load a sound input source from file (if canLoadFile is true) - virtual SampleSource *load(const std::string &file) = 0; + virtual SampleSourcePtr load(const std::string &file) = 0; /// Load a sound input source from stream (if canLoadStream is true) - virtual SampleSource *load(Stream::Stream *input) = 0; + virtual SampleSourcePtr load(Stream::StreamPtr input) = 0; /// Virtual destructor virtual ~SampleSourceLoader() {} }; +typedef boost::shared_ptr SampleSourceLoaderPtr; + }} // namespaces #endif diff --git a/stream/clients/audiere_file.h b/stream/clients/audiere_file.h index 87833e6b3..ec67fd1b7 100644 --- a/stream/clients/audiere_file.h +++ b/stream/clients/audiere_file.h @@ -3,7 +3,6 @@ #include #include -#include "iwrapper.h" namespace Mangle { namespace Stream { @@ -13,11 +12,13 @@ namespace Stream { This lets Audiere read sound files from any generic archive or file manager that supports Mangle streams. */ -class AudiereFile : public audiere::RefImplementation, _SWrapper +class AudiereFile : public audiere::RefImplementation { + StreamPtr inp; + public: - AudiereFile(Stream *inp, bool autoDel=false) - : _SWrapper(inp, autoDel) {} + AudiereFile(StreamPtr _inp) + : inp(_inp) {} /// Read 'count' bytes, return bytes successfully read int read(void *buf, int count) diff --git a/stream/clients/iwrapper.h b/stream/clients/iwrapper.h deleted file mode 100644 index cc12d5b7e..000000000 --- a/stream/clients/iwrapper.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef MANGLE_STREAM_IWRAPPER_H -#define MANGLE_STREAM_IWRAPPER_H - -#include "../stream.h" -#include - -namespace Mangle { -namespace Stream { - -/** A generic wrapper class for a Stream::Stream object. - - This is used by other implementations. - */ -class _SWrapper -{ - private: - bool autoDel; - - protected: - Stream *inp; - - public: - _SWrapper(Stream *_inp, bool _autoDel = false) - : inp(_inp), autoDel(_autoDel) { assert(inp != NULL); } - - virtual ~_SWrapper() { if(autoDel) delete inp; } -}; - -}} // namespaces -#endif diff --git a/stream/clients/ogre_datastream.h b/stream/clients/ogre_datastream.h index 6d0979930..23ab94fd4 100644 --- a/stream/clients/ogre_datastream.h +++ b/stream/clients/ogre_datastream.h @@ -3,7 +3,7 @@ #include #include -#include "iwrapper.h" +#include "../stream.h" namespace Mangle { namespace Stream { @@ -14,8 +14,10 @@ namespace Stream { to make your own modifications if you're working with newer (or older) versions. */ -class MangleDataStream : public Ogre::DataStream, _SWrapper +class MangleDataStream : public Ogre::DataStream { + StreamPtr inp; + void init() { // Get the size, if possible @@ -25,13 +27,12 @@ class MangleDataStream : public Ogre::DataStream, _SWrapper public: /// Constructor without name - MangleDataStream(Stream *inp, bool autoDel=false) - : _SWrapper(inp, autoDel) { init(); } + MangleDataStream(StreamPtr _inp) + : inp(_inp) { init(); } /// Constructor for a named data stream - MangleDataStream(const Ogre::String &name, Stream *inp, bool autoDel=false) - : _SWrapper(inp, autoDel), Ogre::DataStream(name) { init(); } - + MangleDataStream(const Ogre::String &name, StreamPtr _inp) + : inp(_inp), Ogre::DataStream(name) { init(); } // Only implement the DataStream functions we have to implement diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.h index 7790a7542..f04411f98 100644 --- a/stream/filters/buffer_stream.h +++ b/stream/filters/buffer_stream.h @@ -15,7 +15,7 @@ class BufferStream : public MemoryStream std::vector buffer; public: - BufferStream(Stream *input) + BufferStream(StreamPtr input) { // Allocate memory, read the stream into it. Then call set() if(input->hasSize) @@ -62,5 +62,7 @@ class BufferStream : public MemoryStream } }; +typedef boost::shared_ptr BufferStreamPtr; + }} // namespaces #endif diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index 5bd47d978..49f0ec31d 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -10,11 +10,11 @@ namespace Stream { */ class SliceStream : public Stream { - Stream *src; + StreamPtr src; size_t offset, length, pos; public: - SliceStream(Stream *_src, size_t _offset, size_t _length) + SliceStream(StreamPtr _src, size_t _offset, size_t _length) : src(_src), offset(_offset), length(_length), pos(0) { assert(src->hasSize); @@ -50,5 +50,7 @@ class SliceStream : public Stream size_t size() { return length; } }; +typedef boost::shared_ptr SliceStreamPtr; + }} // namespaces #endif diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.h index 1d6f92032..bb4df4d38 100644 --- a/stream/servers/file_stream.h +++ b/stream/servers/file_stream.h @@ -22,5 +22,7 @@ class FileStream : public StdStream ~FileStream() { file.close(); } }; +typedef boost::shared_ptr FileStreamPtr; + }} // namespaces #endif diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 03f7a42f2..94b2f61d9 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -8,9 +8,9 @@ namespace Mangle { namespace Stream { // Do this before the class declaration, since the class itself -// depends on it. TODO: Postponed for later -//class MemoryStream; -//typedef boost::shared_ptr MemoryStreamPtr; +// depends on it. +class MemoryStream; +typedef boost::shared_ptr MemoryStreamPtr; /** A Stream wrapping a memory buffer @@ -88,12 +88,10 @@ class MemoryStream : public Stream No memory is copied during this operation, the new stream is just another 'view' into the same shared memory buffer. - - TODO: Rewrite to use smart pointers */ - MemoryStream* clone(bool setPos=false) const + MemoryStreamPtr clone(bool setPos=false) const { - MemoryStream* res = new MemoryStream(data, length); + MemoryStreamPtr res(new MemoryStream(data, length)); if(setPos) res->seek(pos); return res; } diff --git a/stream/servers/ogre_datastream.h b/stream/servers/ogre_datastream.h index 184fa1668..a5be98c84 100644 --- a/stream/servers/ogre_datastream.h +++ b/stream/servers/ogre_datastream.h @@ -31,5 +31,7 @@ class OgreStream : public Stream bool eof() const { return inp->eof(); } }; +typedef boost::shared_ptr OgreStreamPtr; + }} // namespaces #endif diff --git a/stream/servers/phys_stream.h b/stream/servers/phys_stream.h index 3660391fd..4312ac041 100644 --- a/stream/servers/phys_stream.h +++ b/stream/servers/phys_stream.h @@ -30,5 +30,7 @@ class PhysFile : public Stream bool eof() const { return PHYSFS_eof(file); } }; +typedef boost::shared_ptr PhysFilePtr; + }} // namespaces #endif diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.h index e93295f15..0e91ebb78 100644 --- a/stream/servers/std_stream.h +++ b/stream/servers/std_stream.h @@ -51,5 +51,7 @@ class StdStream : public Stream { return inf->eof(); } }; +typedef boost::shared_ptr StdStreamPtr; + }} // namespaces #endif diff --git a/stream/stream.h b/stream/stream.h index 7116b8e2a..75f84cbd2 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -2,6 +2,7 @@ #define MANGLE_STREAM_INPUT_H #include +#include "../tools/shared_ptr.h" namespace Mangle { namespace Stream { @@ -46,5 +47,7 @@ class Stream virtual bool eof() const = 0; }; +typedef boost::shared_ptr StreamPtr; + }} // namespaces #endif diff --git a/tools/shared_ptr.h b/tools/shared_ptr.h new file mode 100644 index 000000000..00af6084b --- /dev/null +++ b/tools/shared_ptr.h @@ -0,0 +1,3 @@ +// This file should include whatever it needs to define the boost/tr1 +// shared_ptr<> template. +#include diff --git a/vfs/clients/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp index 936eda9b6..0def218db 100644 --- a/vfs/clients/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -38,10 +38,10 @@ static void fill(Ogre::StringVector &out, FileInfoList &in) Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs) { assert(vfs->hasList); - FileInfoList lst = vfs->list("", recursive, dirs); + FileInfoListPtr lst = vfs->list("", recursive, dirs); Ogre::StringVector *res = new Ogre::StringVector; - fill(*res, lst); + fill(*res, *lst); return Ogre::StringVectorPtr(res); } @@ -49,10 +49,10 @@ Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs) Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs) { assert(vfs->hasList); - FileInfoList lst = vfs->list("", recursive, dirs); + FileInfoListPtr lst = vfs->list("", recursive, dirs); Ogre::FileInfoList *res = new Ogre::FileInfoList; - fill(*res, lst); + fill(*res, *lst); return Ogre::FileInfoListPtr(res); } @@ -63,10 +63,10 @@ Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern, bool dirs) { assert(vfs->hasFind); - FileInfoList lst = vfs->find(pattern, recursive, dirs); + FileInfoListPtr lst = vfs->find(pattern, recursive, dirs); Ogre::StringVector *res = new Ogre::StringVector; - fill(*res, lst); + fill(*res, *lst); return Ogre::StringVectorPtr(res); } @@ -76,10 +76,10 @@ Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, bool dirs) { assert(vfs->hasFind); - FileInfoList lst = vfs->find(pattern, recursive, dirs); + FileInfoListPtr lst = vfs->find(pattern, recursive, dirs); Ogre::FileInfoList *res = new Ogre::FileInfoList; - fill(*res, lst); + fill(*res, *lst); return Ogre::FileInfoListPtr(res); } diff --git a/vfs/clients/ogre_archive.h b/vfs/clients/ogre_archive.h index 70e6045c4..84f23eb8a 100644 --- a/vfs/clients/ogre_archive.h +++ b/vfs/clients/ogre_archive.h @@ -3,7 +3,7 @@ #include #include -#include "wrapper.h" +#include "../vfs.h" namespace Mangle { namespace VFS { @@ -15,13 +15,14 @@ namespace VFS { to make your own modifications if you're working with newer (or older) versions. */ -class MangleArchive : public Ogre::Archive, _Wrapper +class MangleArchive : public Ogre::Archive { + VFSPtr vfs; + public: - MangleArchive(VFS *vfs, const std::string &name, - const std::string &archType = "Mangle", - bool autoDel=false) - : _Wrapper(vfs, autoDel), Ogre::Archive(name, archType) {} + MangleArchive(VFSPtr _vfs, const std::string &name, + const std::string &archType = "Mangle") + : vfs(_vfs), Ogre::Archive(name, archType) {} bool isCaseSensitive() const { return vfs->isCaseSensitive; } diff --git a/vfs/clients/wrapper.h b/vfs/clients/wrapper.h deleted file mode 100644 index 357bc8b4f..000000000 --- a/vfs/clients/wrapper.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef MANGLE_VFS_WRAPPER_H -#define MANGLE_VFS_WRAPPER_H - -#include "../vfs.h" -#include - -namespace Mangle { -namespace VFS { - -/** A generic wrapper class for a VFS::VFS object. - - This is used by other implementations. - */ -class _Wrapper -{ - private: - bool autoDel; - - protected: - VFS *vfs; - - public: - _Wrapper(VFS *_vfs, bool _autoDel = false) - : vfs(_vfs), autoDel(_autoDel) { assert(vfs != NULL); } - - virtual ~_Wrapper() { if(autoDel) delete vfs; } -}; - -}} // namespaces -#endif diff --git a/vfs/servers/ogre_vfs.cpp b/vfs/servers/ogre_vfs.cpp index af1f7c963..0fc051a8a 100644 --- a/vfs/servers/ogre_vfs.cpp +++ b/vfs/servers/ogre_vfs.cpp @@ -18,10 +18,10 @@ OgreVFS::OgreVFS(const std::string &_group) group = gm->getWorldResourceGroupName(); } -Mangle::Stream::Stream *OgreVFS::open(const std::string &name) +Mangle::Stream::StreamPtr OgreVFS::open(const std::string &name) { Ogre::DataStreamPtr data = gm->openResource(name, group); - return new Stream::OgreStream(data); + return Strea::StreamPtr(new Stream::OgreStream(data)); } static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) @@ -44,8 +44,8 @@ FileInfoList OgreVFS::list(const std::string& dir, bool dirs) const { Ogre::FileInfoListPtr olist = gm->listResourceFileInfo(group, dirs); - FileInfoList res; - fill(res, *olist, dirs); + FileInfoListPtr res(new FileInfoList); + fill(*res, *olist, dirs); return res; } @@ -54,7 +54,7 @@ FileInfoList OgreVFS::find(const std::string& pattern, bool dirs) const { Ogre::FileInfoListPtr olist = gm->findResourceFileInfo(group, pattern, dirs); - FileInfoList res; - fill(res, *olist, dirs); + FileInfoListPtr res(new FileInfoList); + fill(*res, *olist, dirs); return res; } diff --git a/vfs/servers/ogre_vfs.h b/vfs/servers/ogre_vfs.h index 8fe2cca1c..f212432f8 100644 --- a/vfs/servers/ogre_vfs.h +++ b/vfs/servers/ogre_vfs.h @@ -36,7 +36,7 @@ class OgreVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::Stream *open(const std::string &name); + virtual Stream::StreamPtr open(const std::string &name); /// Check for the existence of a file virtual bool isFile(const std::string &name) const @@ -47,23 +47,23 @@ class OgreVFS : public VFS { return false; } /// This doesn't work. - virtual FileInfo stat(const std::string &name) const - { return FileInfo(); } + virtual FileInfoPtr stat(const std::string &name) const + { return FileInfoPtr(); } /// List all entries in a given directory. A blank dir should be /// interpreted as a the root/current directory of the archive. If /// dirs is true, list directories instead of files. OGRE note: The /// ogre resource systemd does not support recursive listing of /// files. We might make a separate filter for this later. - virtual FileInfoList list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const; + virtual FileInfoListPtr list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const; /// Find files after a given pattern. Wildcards (*) are /// supported. - virtual FileInfoList find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const; + virtual FileInfoListPtr find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const; }; }} // namespaces diff --git a/vfs/servers/physfs_vfs.h b/vfs/servers/physfs_vfs.h index c25a0035c..f85cc54b8 100644 --- a/vfs/servers/physfs_vfs.h +++ b/vfs/servers/physfs_vfs.h @@ -26,8 +26,8 @@ class PhysVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::Stream *open(const std::string &name) - { return new Stream::PhysFile(PHYSFS_openRead(name.c_str())); } + virtual Stream::StreamPtr open(const std::string &name) + { return new Stream::StreamPtr(Stream::PhysFile(PHYSFS_openRead(name.c_str()))); } /// Check for the existence of a file virtual bool isFile(const std::string &name) const @@ -38,15 +38,15 @@ class PhysVFS : public VFS { return PHYSFS_isDirectory(name.c_str()); } /// This doesn't work - virtual FileInfo stat(const std::string &name) const - { assert(0); return FileInfo(); } + virtual FileInfoPtr stat(const std::string &name) const + { assert(0); return FileInfoPtr(); } - virtual FileInfoList list(const std::string& dir = "", + virtual FileInfoListPtr list(const std::string& dir = "", bool recurse=true, bool dirs=false) const { char **files = PHYSFS_enumerateFiles(dir.c_str()); - FileInfoList lst; + FileInfoListPtr lst(new FileInfoList); // Add all teh files int i = 0; @@ -56,14 +56,14 @@ class PhysVFS : public VFS fi.name = files[i]; fi.isDir = false; - lst.push_back(fi); + lst->push_back(fi); } return lst; } - virtual FileInfoList find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const + virtual FileInfoListPtr find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const { assert(0); } }; diff --git a/vfs/vfs.h b/vfs/vfs.h index 4054c6069..c52bcaebb 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.h @@ -29,6 +29,9 @@ struct FileInfo typedef std::vector FileInfoList; +typedef boost::shared_ptr FileInfoPtr; +typedef boost::shared_ptr FileInfoListPtr; + /** An interface to any file system or other provider of named data streams */ @@ -49,9 +52,9 @@ class VFS /// Virtual destructor virtual ~VFS() {} - /// Open a new data stream. Deleting the object should be enough to - /// close it. - virtual Stream::Stream *open(const std::string &name) = 0; + /// Open a new data stream. Deleting the object (letting all the + /// pointers to it go out of scope) should be enough to close it. + virtual Stream::StreamPtr open(const std::string &name) = 0; /// Check for the existence of a file virtual bool isFile(const std::string &name) const = 0; @@ -60,23 +63,25 @@ class VFS virtual bool isDir(const std::string &name) const = 0; /// Get info about a single file - virtual FileInfo stat(const std::string &name) const = 0; + virtual FileInfoPtr stat(const std::string &name) const = 0; /// List all entries in a given directory. A blank dir should be /// interpreted as a the root/current directory of the archive. If /// dirs is true, list directories instead of files. - virtual FileInfoList list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const = 0; + virtual FileInfoListPtr list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const = 0; /// Find files after a given pattern. Wildcards (*) are /// supported. Only valid if 'hasFind' is true. Don't implement your /// own pattern matching here if the backend doesn't support it /// natively; use a filter instead. - virtual FileInfoList find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const = 0; + virtual FileInfoListPtr find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const = 0; }; +typedef boost::shared_ptr VFSPtr; + }} // namespaces #endif From 38501777b0ae8663141397d2777507413ff777db Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 11:31:09 +0100 Subject: [PATCH 032/111] Added getPtr() family to Stream --- stream/filters/slice_stream.h | 20 ++++++++++++++++++++ stream/servers/memory_stream.h | 13 ++++++++++--- stream/stream.h | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index 49f0ec31d..a24a66e16 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -22,6 +22,11 @@ class SliceStream : public Stream // Make sure we can actually fit inside the source stream assert(src->size() <= offset+length); + + isSeekable = true; + hasPosition = true; + hasSize = true; + hasPtr = src->hasPtr; } size_t read(void *buf, size_t count) @@ -48,6 +53,21 @@ class SliceStream : public Stream bool eof() { return pos == length; } size_t tell() { return pos; } size_t size() { return length; } + + void *getPtr() { return getPtr(0, length); } + void *getPtr(size_t size) { return getPtr(pos, size); } + void *getPtr(size_t pos, size_t size) + { + // Boundry checks on pos and size. Bounding the size is + // important even when getting pointers, as the source stream + // may use the size parameter for something (such as memory + // mapping or buffering.) + if(pos > length) pos = length; + if(pos+size > length) size = length-pos; + + // Ask the source to kindly give us a pointer + return src->getPtr(offset+pos, size); + } }; typedef boost::shared_ptr SliceStreamPtr; diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 94b2f61d9..23842e09b 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -37,6 +37,7 @@ class MemoryStream : public Stream isSeekable = true; hasPosition = true; hasSize = true; + hasPtr = true; } size_t read(void *buf, size_t count) @@ -69,6 +70,15 @@ class MemoryStream : public Stream size_t size() const { return length; } bool eof() const { return pos == length; } + /// Get the base pointer to the entire buffer + const void *getPtr() const { return data; } + const void *getPtr(size_t size) const { return ((char*)data)+pos; } + const void *getPtr(size_t pos, size_t size) + { + if(pos > length) pos = length; + return ((char*)data)+pos; + } + // New members in MemoryStream: /// Set a new buffer and length. This will rewind the position to zero. @@ -79,9 +89,6 @@ class MemoryStream : public Stream pos = 0; } - /// Get the base pointer to the entire buffer - const void *getPtr() const { return data; } - /// Clone this memory stream /** Make a new stream of the same buffer. If setPos is true, we also set the clone's position to be the same as ours. diff --git a/stream/stream.h b/stream/stream.h index 75f84cbd2..704abc88a 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -3,6 +3,7 @@ #include #include "../tools/shared_ptr.h" +#include namespace Mangle { namespace Stream { @@ -22,6 +23,14 @@ class Stream /// If true, size() works bool hasSize; + /// If true, the getPtr() functions work + bool hasPtr; + + /// Initialize all bools to false by default + Stream() : + isSeekable(false), hasPosition(false), hasSize(false), + hasPtr(false) {} + /// Virtual destructor virtual ~Stream() {} @@ -45,6 +54,19 @@ class Stream /// Returns true if the stream is empty virtual bool eof() const = 0; + + /// Return a pointer to the entire stream. This function (and the + /// other getPtr() variants below) should only be implemented for + /// memory-based streams where using them would be an optimization. + virtual void *getPtr() const { assert(0); } + + /// Get a pointer to a memory region of 'size' bytes from the + /// current position. + virtual void *getPtr(size_t size) const { assert(0); } + + /// Get a pointer to a memory region of 'size' bytes starting from + /// position 'pos' + virtual void *getPtr(size_t pos, size_t size) const { assert(0); } }; typedef boost::shared_ptr StreamPtr; From eaf93691d50ecc97e70550e6dd0dfc4c36b18c6c Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 14:34:46 +0100 Subject: [PATCH 033/111] Nearly finished shared_ptr, and getPtr(). Tests not rewritten yet. --- sound/outputs/openal_out.cpp | 28 +++++++++++++-------- sound/outputs/openal_out.h | 10 ++++---- sound/source.h | 10 +------- sound/sources/audiere_source.cpp | 2 +- sound/sources/audiere_source.h | 2 +- sound/sources/ffmpeg_source.h | 2 +- sound/sources/loadertemplate.h | 2 +- sound/sources/memsource.h | 42 +++++++++++++++----------------- 8 files changed, 48 insertions(+), 50 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 649a56aef..910690156 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -108,10 +108,6 @@ void OpenAL_Sound::setRepeat(bool rep) alSourcei(Source, AL_LOOPING, rep?AL_TRUE:AL_FALSE); } -void OpenAL_Sound::setup() -{ -} - // Constructor used for cloned sounds OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) : refCnt(ref), bufferID(buf) @@ -125,19 +121,31 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) alSourcei(inst, AL_BUFFER, bufferID); } -OpenAL_Sound::OpenAL_Sound(SampleSource *input) +OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) { // Get the format int fmt, rate; getALFormat(inp, fmt, rate); - // Read the entire stream into a buffer - BufferStream buf(input); - - // Move the data into OpenAL + // Set up the OpenAL buffer alGenBuffers(1, &bufferID); assert(bufferID != 0); - alBufferData(bufferID, fmt, &buf.getPtr(), buf.size(), rate); + + // Does the stream support pointer operations? + if(input->hasPtr) + { + // If so, we can read the data directly from the stream + alBufferData(bufferID, fmt, &input.getPtr(), input.size(), rate); + } + else + { + // Read the entire stream into a temporary buffer first + BufferStream buf(input); + + // Then copy that into OpenAL + alBufferData(bufferID, fmt, &buf.getPtr(), buf.size(), rate); + } + checkALError("loading sound buffer"); // Create a source diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index a0b8ed412..115c3c13e 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -22,7 +22,7 @@ class OpenAL_Sound : public Sound int *refCnt; public: - OpenAL_Sound(SampleSource *input); + OpenAL_Sound(SampleSourcePtr input); OpenAL_Sound(ALuint buf, int *ref); // Used for cloning ~OpenAL_Sound(); @@ -83,10 +83,10 @@ class OpenAL_Factory : public SoundFactory } } - Sound *load(const std::string &file, bool stream=false) { assert(0); } - Sound *load(Stream::Stream *input, bool stream=false) { assert(0); } - Sound *load(SampleSource* input, bool stream=false) - { return new OpenAL_Sound(input); } + SoundPtr load(const std::string &file, bool stream=false) { assert(0); } + SoundPtr load(Stream::StreamPtr input, bool stream=false) { assert(0); } + SoundPtr load(SampleSourcePtr input, bool stream=false) + { return SoundPtr(new OpenAL_Sound(input)); } void update() {} setListenerPos(float x, float y, float z, diff --git a/sound/source.h b/sound/source.h index 0301f4bae..f987dfe4f 100644 --- a/sound/source.h +++ b/sound/source.h @@ -17,15 +17,7 @@ class SampleSource : public Stream::Stream bool isEof; public: - SampleSource() - { - // These are usually not needed for sound data - isSeekable = false; - hasPosition = false; - hasSize = false; - - isEof = false; - } + SampleSource() : isEof(false) {} /// Get the sample rate, number of channels, and bits per /// sample. NULL parameters are ignored. diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index d99c6c4ee..1a0dfe8dc 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -109,7 +109,7 @@ AudiereSource::AudiereSource(const std::string &file) setup(); } -AudiereSource::AudiereSource(Stream::Stream *input) +AudiereSource::AudiereSource(Stream::StreamPtr input) { // Use our Stream::AudiereFile implementation to convert a Mangle // 'Stream' to an Audiere 'File' diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h index 1348e112c..671595a1a 100644 --- a/sound/sources/audiere_source.h +++ b/sound/sources/audiere_source.h @@ -33,7 +33,7 @@ class AudiereSource : public SampleSource AudiereSource(const std::string &file); /// Decode the given sound stream - AudiereSource(Stream::Stream *src); + AudiereSource(Stream::StreamPtr src); /// Read directly from an existing audiere::SampleSource AudiereSource(audiere::SampleSourcePtr src); diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h index 5317c11c0..d185098fd 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.h @@ -28,7 +28,7 @@ class FFMpegSource : public SampleSource FFMpegSource(const std::string &file); /// Decode the given sound stream (not supported by FFmpeg) - FFMpegSource(Stream::Stream *src) { assert(0); } + FFMpegSource(Stream::StreamPtr src) { assert(0); } ~FFMpegSource(); diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h index 0b668fffa..f1ebea2d5 100644 --- a/sound/sources/loadertemplate.h +++ b/sound/sources/loadertemplate.h @@ -16,7 +16,7 @@ class SSL_Template : public SampleSourceLoader return new SourceT(file); } - SampleSource *load(Stream::Stream *input) + SampleSource *load(Stream::StreamPtr input) { assert(canLoadStream); return new SourceT(input); diff --git a/sound/sources/memsource.h b/sound/sources/memsource.h index 1e38d9052..72624b899 100644 --- a/sound/sources/memsource.h +++ b/sound/sources/memsource.h @@ -6,19 +6,21 @@ namespace Mangle { namespace Sound { -/// A sample source reading directly from a memory buffer -class MemorySource : public SampleSource +/// A class for reading raw samples directly from a stream. +class Stream2Samples : public SampleSource { - char *buf; - size_t len; - size_t pos; - int32_t rate, channels, bits; + Stream::StreamPtr inp; public: - MemorySource(void *_buf, size_t _len, int32_t _rate, int32_t _channels, int32_t _bits) - : len(_len), pos(0), rate(_rate), channels(_channels), bits(_bits) - { buf = (char*)_buf; } + Stream2Samples(Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits) + : inp(_inp), rate(_rate), channels(_channels), bits(_bits) + { + isSeekable = inp->isSeekable; + hasPosition = inp->hasPosition; + hasSize = inp->hasSize; + hasPtr = inp->hasPtr; + } /// Get the sample rate, number of channels, and bits per /// sample. NULL parameters are ignored. @@ -29,20 +31,16 @@ class MemorySource : public SampleSource if(_bits) *_bits = bits; } - bool eof() const { return pos == len; } - size_t read(void *out, size_t count) - { - assert(len >= pos); - - if(count > (len-pos)) - count = len-pos; - - if(count) memcpy(out, buf+pos, count); - pos += count; - - return count; - } + { return inp->read(out, count); } + + void seek(size_t pos) { inp->seek(pos); } + size_t tell() const { return inp->tell(); } + size_t size() const { return inp->size(); } + bool eof() const { return inp->eof(); } + void *getPtr() const { return inp->getPtr(); } + void *getPtr(size_t size) const { return inp->getPtr(size); } + void *getPtr(size_t pos, size_t size) const { return inp->getPtr(pos, size); } }; }} // namespaces From c5316804b566cd905a5ecf621eb8fdc6c4a42c20 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 16:39:11 +0100 Subject: [PATCH 034/111] Fixed and tested all Stream tests --- .../sources/{memsource.h => stream_source.h} | 10 ++--- stream/clients/audiere_file.h | 2 + stream/clients/ogre_datastream.h | 6 +-- stream/filters/slice_stream.h | 14 +++---- stream/servers/file_stream.h | 2 +- stream/servers/memory_stream.h | 5 ++- stream/servers/std_stream.h | 6 +-- stream/stream.h | 6 +-- stream/tests/Makefile | 16 ++++--- stream/tests/audiere_client_test.cpp | 4 +- ...buffer_test.cpp => buffer_filter_test.cpp} | 4 +- stream/tests/file_server_test.cpp | 18 ++++++++ ...memory_test.cpp => memory_server_test.cpp} | 4 +- stream/tests/ogre_client_test.cpp | 6 +-- stream/tests/slice_filter_test.cpp | 42 +++++++++++++++++++ tools/shared_ptr.h | 2 +- 16 files changed, 109 insertions(+), 38 deletions(-) rename sound/sources/{memsource.h => stream_source.h} (81%) rename stream/tests/{buffer_test.cpp => buffer_filter_test.cpp} (92%) create mode 100644 stream/tests/file_server_test.cpp rename stream/tests/{memory_test.cpp => memory_server_test.cpp} (89%) create mode 100644 stream/tests/slice_filter_test.cpp diff --git a/sound/sources/memsource.h b/sound/sources/stream_source.h similarity index 81% rename from sound/sources/memsource.h rename to sound/sources/stream_source.h index 72624b899..278f701e8 100644 --- a/sound/sources/memsource.h +++ b/sound/sources/stream_source.h @@ -1,5 +1,5 @@ -#ifndef MANGLE_SOUND_MEMSOURCE_H -#define MANGLE_SOUND_MEMSOURCE_H +#ifndef MANGLE_SOUND_STREAMSOURCE_H +#define MANGLE_SOUND_STREAMSOURCE_H #include "../source.h" @@ -38,9 +38,9 @@ class Stream2Samples : public SampleSource size_t tell() const { return inp->tell(); } size_t size() const { return inp->size(); } bool eof() const { return inp->eof(); } - void *getPtr() const { return inp->getPtr(); } - void *getPtr(size_t size) const { return inp->getPtr(size); } - void *getPtr(size_t pos, size_t size) const { return inp->getPtr(pos, size); } + const void *getPtr() { return inp->getPtr(); } + const void *getPtr(size_t size) { return inp->getPtr(size); } + const void *getPtr(size_t pos, size_t size) { return inp->getPtr(pos, size); } }; }} // namespaces diff --git a/stream/clients/audiere_file.h b/stream/clients/audiere_file.h index ec67fd1b7..670b36ffa 100644 --- a/stream/clients/audiere_file.h +++ b/stream/clients/audiere_file.h @@ -4,6 +4,8 @@ #include #include +#include "../stream.h" + namespace Mangle { namespace Stream { diff --git a/stream/clients/ogre_datastream.h b/stream/clients/ogre_datastream.h index 23ab94fd4..5369ed11a 100644 --- a/stream/clients/ogre_datastream.h +++ b/stream/clients/ogre_datastream.h @@ -14,7 +14,7 @@ namespace Stream { to make your own modifications if you're working with newer (or older) versions. */ -class MangleDataStream : public Ogre::DataStream +class Mangle2OgreStream : public Ogre::DataStream { StreamPtr inp; @@ -27,11 +27,11 @@ class MangleDataStream : public Ogre::DataStream public: /// Constructor without name - MangleDataStream(StreamPtr _inp) + Mangle2OgreStream(StreamPtr _inp) : inp(_inp) { init(); } /// Constructor for a named data stream - MangleDataStream(const Ogre::String &name, StreamPtr _inp) + Mangle2OgreStream(const Ogre::String &name, StreamPtr _inp) : inp(_inp), Ogre::DataStream(name) { init(); } // Only implement the DataStream functions we have to implement diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index a24a66e16..d792b57d6 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -21,7 +21,7 @@ class SliceStream : public Stream assert(src->isSeekable); // Make sure we can actually fit inside the source stream - assert(src->size() <= offset+length); + assert(src->size() >= offset+length); isSeekable = true; hasPosition = true; @@ -50,13 +50,13 @@ class SliceStream : public Stream if(pos > length) pos = length; } - bool eof() { return pos == length; } - size_t tell() { return pos; } - size_t size() { return length; } + bool eof() const { return pos == length; } + size_t tell() const { return pos; } + size_t size() const { return length; } - void *getPtr() { return getPtr(0, length); } - void *getPtr(size_t size) { return getPtr(pos, size); } - void *getPtr(size_t pos, size_t size) + const void *getPtr() { return getPtr(0, length); } + const void *getPtr(size_t size) { return getPtr(pos, size); } + const void *getPtr(size_t pos, size_t size) { // Boundry checks on pos and size. Bounding the size is // important even when getting pointers, as the source stream diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.h index bb4df4d38..012cb3a78 100644 --- a/stream/servers/file_stream.h +++ b/stream/servers/file_stream.h @@ -17,7 +17,7 @@ class FileStream : public StdStream FileStream(const std::string &name) : StdStream(&file) { - file.open(name, ios::binary); + file.open(name.c_str(), std::ios::binary); } ~FileStream() { file.close(); } }; diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 23842e09b..d77779878 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -3,6 +3,7 @@ #include #include "../stream.h" +#include namespace Mangle { namespace Stream { @@ -71,8 +72,8 @@ class MemoryStream : public Stream bool eof() const { return pos == length; } /// Get the base pointer to the entire buffer - const void *getPtr() const { return data; } - const void *getPtr(size_t size) const { return ((char*)data)+pos; } + const void *getPtr() { return data; } + const void *getPtr(size_t size) { return ((char*)data)+pos; } const void *getPtr(size_t pos, size_t size) { if(pos > length) pos = length; diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.h index 0e91ebb78..d4b56a9ae 100644 --- a/stream/servers/std_stream.h +++ b/stream/servers/std_stream.h @@ -1,5 +1,5 @@ -#ifndef MANGLE_STREAM_FILESERVER_H -#define MANGLE_STREAM_FILESERVER_H +#ifndef MANGLE_STREAM_STDIOSERVER_H +#define MANGLE_STREAM_STDIOSERVER_H #include "../stream.h" #include @@ -41,7 +41,7 @@ class StdStream : public Stream { // Use the standard iostream size hack, terrible as it is. std::streampos pos = inf->tellg(); - inf->seekg(0, ios_base::end); + inf->seekg(0, std::ios::end); size_t res = inf->tellg(); inf->seekg(pos); return res; diff --git a/stream/stream.h b/stream/stream.h index 704abc88a..309b0b33a 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -58,15 +58,15 @@ class Stream /// Return a pointer to the entire stream. This function (and the /// other getPtr() variants below) should only be implemented for /// memory-based streams where using them would be an optimization. - virtual void *getPtr() const { assert(0); } + virtual const void *getPtr() { assert(0); } /// Get a pointer to a memory region of 'size' bytes from the /// current position. - virtual void *getPtr(size_t size) const { assert(0); } + virtual const void *getPtr(size_t size) { assert(0); } /// Get a pointer to a memory region of 'size' bytes starting from /// position 'pos' - virtual void *getPtr(size_t pos, size_t size) const { assert(0); } + virtual const void *getPtr(size_t pos, size_t size) { assert(0); } }; typedef boost::shared_ptr StreamPtr; diff --git a/stream/tests/Makefile b/stream/tests/Makefile index c86a7397f..d95908afb 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,21 +1,27 @@ GCC=g++ -I../ -all: ogre_client_test dummy_test audiere_client_test +all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_AUDIERE=-laudiere -ogre_client_test: ogre_client_test.cpp ../stream.h ../clients/iwrapper.h ../clients/ogre_datastream.h +ogre_client_test: ogre_client_test.cpp ../stream.h ../clients/ogre_datastream.h $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/iwrapper.h ../clients/audiere_file.h ../clients/audiere_file.cpp +audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/audiere_file.h ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -memory_test: memory_test.cpp ../stream.h ../servers/memory_stream.h +file_server_test: file_server_test.cpp ../stream.h ../servers/file_stream.h ../servers/std_stream.h $(GCC) $< -o $@ -buffer_test: buffer_test.cpp ../stream.h ../servers/memory_stream.h ../filters/buffer_stream.h +memory_server_test: memory_server_test.cpp ../stream.h ../servers/memory_stream.h + $(GCC) $< -o $@ + +buffer_filter_test: buffer_filter_test.cpp ../stream.h ../servers/memory_stream.h ../filters/buffer_stream.h + $(GCC) $< -o $@ + +slice_filter_test: slice_filter_test.cpp ../stream.h ../servers/memory_stream.h ../filters/slice_stream.h $(GCC) $< -o $@ clean: diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp index 01f548714..dd4dfe4ad 100644 --- a/stream/tests/audiere_client_test.cpp +++ b/stream/tests/audiere_client_test.cpp @@ -11,8 +11,8 @@ int main() { char str[12]; memset(str, 0, 12); - Stream *inp = new MemoryStream("hello world", 11); - FilePtr p(new AudiereFile(inp, true)); + StreamPtr inp(new MemoryStream("hello world", 11)); + FilePtr p(new AudiereFile(inp)); cout << "pos=" << p->tell() << endl; p->read(str, 2); cout << "2 bytes: " << str << endl; diff --git a/stream/tests/buffer_test.cpp b/stream/tests/buffer_filter_test.cpp similarity index 92% rename from stream/tests/buffer_test.cpp rename to stream/tests/buffer_filter_test.cpp index 24bb7e070..b6f43e99b 100644 --- a/stream/tests/buffer_test.cpp +++ b/stream/tests/buffer_filter_test.cpp @@ -8,8 +8,8 @@ using namespace std; int main() { - Stream *orig = new MemoryStream("hello world", 11); - Stream *inp = new BufferStream(orig); + StreamPtr orig (new MemoryStream("hello world", 11)); + StreamPtr inp (new BufferStream(orig)); cout << "Size: " << inp->size() << endl; cout << "Pos: " << inp->tell() << "\nSeeking...\n"; diff --git a/stream/tests/file_server_test.cpp b/stream/tests/file_server_test.cpp new file mode 100644 index 000000000..1c2505158 --- /dev/null +++ b/stream/tests/file_server_test.cpp @@ -0,0 +1,18 @@ +#include "../servers/file_stream.h" +#include + +using namespace Mangle::Stream; +using namespace std; + +int main() +{ + StreamPtr inp(new FileStream("file_server_test.cpp")); + + char buf[21]; + buf[20] = 0; + cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl; + inp->read(buf, 20); + cout << "First 20 bytes: " << buf << endl; + cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl; + return 0; +} diff --git a/stream/tests/memory_test.cpp b/stream/tests/memory_server_test.cpp similarity index 89% rename from stream/tests/memory_test.cpp rename to stream/tests/memory_server_test.cpp index 7e7f34075..b9f21b9b1 100644 --- a/stream/tests/memory_test.cpp +++ b/stream/tests/memory_server_test.cpp @@ -8,7 +8,7 @@ using namespace std; int main() { - Stream *inp = new MemoryStream("hello world", 11); + Stream* inp = new MemoryStream("hello world\0", 12); cout << "Size: " << inp->size() << endl; cout << "Pos: " << inp->tell() << "\nSeeking...\n"; @@ -35,6 +35,8 @@ int main() cout << "Result: " << data << endl; cout << "Eof: " << inp->eof() << endl; cout << "Pos: " << inp->tell() << endl; + + cout << "Entire stream from pointer: " << (char*)inp->getPtr() << endl; return 0; } diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp index 530113362..a89589d7c 100644 --- a/stream/tests/ogre_client_test.cpp +++ b/stream/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ #include "../servers/memory_stream.h" -#include "ogre_datastream.h" +#include "../clients/ogre_datastream.h" #include using namespace Mangle::Stream; @@ -8,8 +8,8 @@ using namespace std; int main() { - Stream *inp = new MemoryStream("hello world", 11); - DataStreamPtr p(new MangleDataStream("hello", inp, true)); + StreamPtr inp(new MemoryStream("hello world", 11)); + DataStreamPtr p(new Mangle2OgreStream("hello", inp)); cout << "Name: " << p->getName() << endl; cout << "As string: " << p->getAsString() << endl; cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; diff --git a/stream/tests/slice_filter_test.cpp b/stream/tests/slice_filter_test.cpp new file mode 100644 index 000000000..ff384c1bb --- /dev/null +++ b/stream/tests/slice_filter_test.cpp @@ -0,0 +1,42 @@ +#include +#include + +#include "../filters/slice_stream.h" +#include "../servers/memory_stream.h" + +using namespace Mangle::Stream; +using namespace std; + +void test(StreamPtr inp) +{ + cout << "Size: " << inp->size() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking...\n"; + char data[6]; + memset(data, 0, 6); + cout << "Reading " << inp->read(data, 6) << " bytes\n"; + cout << "Result: " << data << endl; + cout << "Pos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << endl; + inp->seek(2); + cout << "Seeking:\nPos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Reading " << inp->read(data, 6) << " bytes\n"; + cout << "Result: " << data << endl; + cout << "Pos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Entire stream as pointer: " << (char*)inp->getPtr() << endl; +} + +int main() +{ + StreamPtr orig (new MemoryStream("hello\0world\0", 12)); + StreamPtr slice1 (new SliceStream(orig,0,6)); + StreamPtr slice2 (new SliceStream(orig,6,6)); + + cout << "\nSlice 1:\n--------\n"; + test(slice1); + cout << "\nSlice 2:\n--------\n"; + test(slice2); + + return 0; +} diff --git a/tools/shared_ptr.h b/tools/shared_ptr.h index 00af6084b..da0b399bd 100644 --- a/tools/shared_ptr.h +++ b/tools/shared_ptr.h @@ -1,3 +1,3 @@ // This file should include whatever it needs to define the boost/tr1 // shared_ptr<> template. -#include +#include From 9e332c40671f8db0abab3f889f558d7450d03fa3 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 17:06:41 +0100 Subject: [PATCH 035/111] Fixed and tested all VFS tests --- vfs/clients/ogre_archive.cpp | 4 +-- vfs/clients/ogre_archive.h | 5 ++-- vfs/servers/ogre_vfs.cpp | 9 +++--- vfs/servers/physfs_vfs.h | 2 +- vfs/tests/Makefile | 2 +- vfs/tests/dummy_test.cpp | 8 +++-- vfs/tests/dummy_vfs.cpp | 50 +++++++++++++++++--------------- vfs/tests/ogre_client_test.cpp | 2 +- vfs/tests/ogre_resource_test.cpp | 2 +- vfs/tests/server_common.cpp | 2 +- 10 files changed, 46 insertions(+), 40 deletions(-) diff --git a/vfs/clients/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp index 0def218db..e9612d7d9 100644 --- a/vfs/clients/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -7,8 +7,8 @@ using namespace Mangle::Stream; Ogre::DataStreamPtr MangleArchive::open(const Ogre::String& filename) const { - return Ogre::DataStreamPtr(new MangleDataStream - (filename, vfs->open(filename), true)); + return Ogre::DataStreamPtr(new Mangle2OgreStream + (filename, vfs->open(filename))); } static void fill(Ogre::FileInfoList &out, FileInfoList &in) diff --git a/vfs/clients/ogre_archive.h b/vfs/clients/ogre_archive.h index 84f23eb8a..22964dc35 100644 --- a/vfs/clients/ogre_archive.h +++ b/vfs/clients/ogre_archive.h @@ -26,7 +26,8 @@ class MangleArchive : public Ogre::Archive bool isCaseSensitive() const { return vfs->isCaseSensitive; } - // These do nothing. You have to load / unload the archive manually. + // These do nothing. You have to load / unload the archive in the + // constructor/destructor. void load() {} void unload() {} @@ -34,7 +35,7 @@ class MangleArchive : public Ogre::Archive { return vfs->isFile(filename); } time_t getModifiedTime(const Ogre::String& filename) - { return vfs->stat(filename).time; } + { return vfs->stat(filename)->time; } Ogre::DataStreamPtr open(const Ogre::String& filename) const; diff --git a/vfs/servers/ogre_vfs.cpp b/vfs/servers/ogre_vfs.cpp index 0fc051a8a..ab496a192 100644 --- a/vfs/servers/ogre_vfs.cpp +++ b/vfs/servers/ogre_vfs.cpp @@ -2,6 +2,7 @@ #include "../../stream/servers/ogre_datastream.h" using namespace Mangle::VFS; +using namespace Mangle::Stream; OgreVFS::OgreVFS(const std::string &_group) : group(_group) @@ -18,10 +19,10 @@ OgreVFS::OgreVFS(const std::string &_group) group = gm->getWorldResourceGroupName(); } -Mangle::Stream::StreamPtr OgreVFS::open(const std::string &name) +StreamPtr OgreVFS::open(const std::string &name) { Ogre::DataStreamPtr data = gm->openResource(name, group); - return Strea::StreamPtr(new Stream::OgreStream(data)); + return StreamPtr(new OgreStream(data)); } static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) @@ -39,7 +40,7 @@ static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) } } -FileInfoList OgreVFS::list(const std::string& dir, +FileInfoListPtr OgreVFS::list(const std::string& dir, bool recurse, bool dirs) const { @@ -49,7 +50,7 @@ FileInfoList OgreVFS::list(const std::string& dir, return res; } -FileInfoList OgreVFS::find(const std::string& pattern, +FileInfoListPtr OgreVFS::find(const std::string& pattern, bool recursive, bool dirs) const { diff --git a/vfs/servers/physfs_vfs.h b/vfs/servers/physfs_vfs.h index f85cc54b8..49acc398d 100644 --- a/vfs/servers/physfs_vfs.h +++ b/vfs/servers/physfs_vfs.h @@ -27,7 +27,7 @@ class PhysVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. virtual Stream::StreamPtr open(const std::string &name) - { return new Stream::StreamPtr(Stream::PhysFile(PHYSFS_openRead(name.c_str()))); } + { return Stream::StreamPtr(new Stream::PhysFile(PHYSFS_openRead(name.c_str()))); } /// Check for the existence of a file virtual bool isFile(const std::string &name) const diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index 4595c82ea..c9963cfa1 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -6,7 +6,7 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_PHYSFS=-lphysfs -ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../clients/wrapper.h ../clients/ogre_archive.h ../clients/ogre_archive.cpp +ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../clients/ogre_archive.h ../clients/ogre_archive.cpp $(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) ogre_resource_test: ogre_resource_test.cpp diff --git a/vfs/tests/dummy_test.cpp b/vfs/tests/dummy_test.cpp index b65f38335..8d7fc7dd6 100644 --- a/vfs/tests/dummy_test.cpp +++ b/vfs/tests/dummy_test.cpp @@ -5,7 +5,7 @@ using namespace std; -void print(FileInfo inf) +void print(FileInfo &inf) { cout << "name: " << inf.name << endl; cout << "basename: " << inf.basename << endl; @@ -13,12 +13,14 @@ void print(FileInfo inf) cout << "size: " << inf.size << endl; cout << "time: " << inf.time << endl; } +void print(FileInfoPtr inf) { print(*inf); } -void print(FileInfoList lst) +void print(FileInfoList &lst) { for(int i=0; isize() << endl; return 0; diff --git a/vfs/tests/dummy_vfs.cpp b/vfs/tests/dummy_vfs.cpp index e7c77a753..7a39da932 100644 --- a/vfs/tests/dummy_vfs.cpp +++ b/vfs/tests/dummy_vfs.cpp @@ -3,9 +3,10 @@ #include #include -#include "../../stream/tests/dummy_input.cpp" +#include "../../stream/servers/memory_stream.h" using namespace Mangle::VFS; +using namespace Mangle::Stream; class DummyVFS : public VFS { @@ -13,14 +14,15 @@ public: DummyVFS() { hasFind = false; + hasList = true; isCaseSensitive = true; } // We only support opening 'file1' at the moment. - Mangle::Stream::Stream *open(const std::string &name) + StreamPtr open(const std::string &name) { assert(name == "file1"); - return new DummyInput(); + return StreamPtr(new MemoryStream("hello world", 11)); } bool isFile(const std::string &name) const @@ -35,31 +37,31 @@ public: } /// Get info about a single file - FileInfo stat(const std::string &name) const + FileInfoPtr stat(const std::string &name) const { - FileInfo fi; - fi.name = name; - fi.time = 0; + FileInfoPtr fi(new FileInfo); + fi->name = name; + fi->time = 0; if(isFile(name)) { if(name == "dir/file2") { - fi.basename = "file2"; - fi.size = 2; + fi->basename = "file2"; + fi->size = 2; } else { - fi.basename = "file1"; - fi.size = 1; + fi->basename = "file1"; + fi->size = 1; } - fi.isDir = false; + fi->isDir = false; } else if(isDir(name)) { - fi.basename = "dir"; - fi.isDir = true; - fi.size = 0; + fi->basename = "dir"; + fi->isDir = true; + fi->size = 0; } else assert(0); @@ -69,13 +71,13 @@ public: /// List all entries in a given directory. A blank dir should be /// interpreted as a the root/current directory of the archive. If /// dirs is true, list directories instead of files. - virtual FileInfoList list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const + virtual FileInfoListPtr list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const { assert(dir == ""); - FileInfoList fl; + FileInfoListPtr fl(new FileInfoList); FileInfo fi; @@ -86,14 +88,14 @@ public: fi.isDir = false; fi.size = 1; fi.time = 0; - fl.push_back(fi); + fl->push_back(fi); if(recurse) { fi.name = "dir/file2"; fi.basename = "file2"; fi.size = 2; - fl.push_back(fi); + fl->push_back(fi); } } else @@ -103,13 +105,13 @@ public: fi.isDir = true; fi.size = 0; fi.time = 0; - fl.push_back(fi); + fl->push_back(fi); } return fl; } - FileInfoList find(const std::string& pattern, + FileInfoListPtr find(const std::string& pattern, bool recursive=true, bool dirs=false) const - { assert(0); return FileInfoList(); } + { assert(0); } }; diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp index d38add4da..542ac9c7e 100644 --- a/vfs/tests/ogre_client_test.cpp +++ b/vfs/tests/ogre_client_test.cpp @@ -17,7 +17,7 @@ void print(StringVectorPtr lst) int main() { - VFS *vfs = new DummyVFS(); + VFSPtr vfs(new DummyVFS()); MangleArchive arc(vfs, "dummy"); cout << "Case: " << arc.isCaseSensitive() << endl; diff --git a/vfs/tests/ogre_resource_test.cpp b/vfs/tests/ogre_resource_test.cpp index eadb1153f..8965adaa1 100644 --- a/vfs/tests/ogre_resource_test.cpp +++ b/vfs/tests/ogre_resource_test.cpp @@ -6,7 +6,7 @@ This isn't really a test of our implementation, but a test of using the Ogre resource system to find files. If the Ogre interface changes and you have to change this test, you will have to change - the ogre_vfs.cpp implementation equivalently. + the servers/ogre_vfs.cpp implementation equivalently. */ diff --git a/vfs/tests/server_common.cpp b/vfs/tests/server_common.cpp index ff16ac7f2..1834bc25a 100644 --- a/vfs/tests/server_common.cpp +++ b/vfs/tests/server_common.cpp @@ -14,7 +14,7 @@ void find(VFS &vfs, const std::string &file) return; } - Stream *data = vfs.open(file); + StreamPtr data = vfs.open(file); cout << "Size: " << data->size() << endl; From 56f9daed9685e2b6f36b15c300f61cfc006030fd Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 18:42:35 +0100 Subject: [PATCH 036/111] Combined OpenAL+Audiere sound test now compiles and runs, but segfaults --- sound/filters/input_filter.h | 9 ++- sound/filters/openal_audiere.h | 4 +- sound/output.h | 6 +- sound/outputs/openal_out.cpp | 61 +++++++++++++++++--- sound/outputs/openal_out.h | 49 +++------------- sound/source.h | 4 +- sound/sources/audiere_source.cpp | 8 ++- sound/sources/audiere_source.h | 10 ++-- sound/sources/loadertemplate.h | 10 ++-- sound/tests/Makefile | 12 +--- sound/tests/common.cpp | 89 ----------------------------- sound/tests/openal_audiere_test.cpp | 49 +++++++++++++++- stream/filters/buffer_stream.h | 3 + 13 files changed, 142 insertions(+), 172 deletions(-) delete mode 100644 sound/tests/common.cpp diff --git a/sound/filters/input_filter.h b/sound/filters/input_filter.h index 17dda5534..2b4486b75 100644 --- a/sound/filters/input_filter.h +++ b/sound/filters/input_filter.h @@ -40,7 +40,6 @@ class InputFilter : public SoundFactory // Set capabilities needsUpdate = snd->needsUpdate; has3D = snd->has3D; - canRepeatStream = snd->canRepeatStream; canLoadStream = inp->canLoadStream; // Both these should be true, or the use of this class is pretty @@ -51,13 +50,13 @@ class InputFilter : public SoundFactory } virtual SoundPtr load(const std::string &file) - { return load(inp->load(file), stream); } + { return loadRaw(inp->load(file)); } virtual SoundPtr load(Stream::StreamPtr input) - { return load(inp->load(input), stream); } + { return loadRaw(inp->load(input)); } - virtual SoundPtr load(SampleSourcePtr input) - { return snd->load(input, stream); } + virtual SoundPtr loadRaw(SampleSourcePtr input) + { return snd->loadRaw(input); } virtual void update() { snd->update(); } virtual void setListenerPos(float x, float y, float z, diff --git a/sound/filters/openal_audiere.h b/sound/filters/openal_audiere.h index 9a2bfcf8c..a1b2e6b64 100644 --- a/sound/filters/openal_audiere.h +++ b/sound/filters/openal_audiere.h @@ -15,8 +15,8 @@ class OpenAL_Audiere_Factory : public InputFilter public: OpenAL_Audiere_Factory() { - set(new OpenAL_Factory, - new AudiereLoader); + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new AudiereLoader)); } }; diff --git a/sound/output.h b/sound/output.h index dfd190241..6bcd93237 100644 --- a/sound/output.h +++ b/sound/output.h @@ -39,7 +39,7 @@ class Sound virtual void pause() = 0; /// Check if the sound is still playing - virtual bool isPlaying() = 0 const; + virtual bool isPlaying() const = 0; /// Set the volume. The parameter must be between 0.0 and 1.0. virtual void setVolume(float) = 0; @@ -120,7 +120,7 @@ class SoundFactory large files, but they are not required to. @return a new Sound object */ - virtual SoundPtr load(SampleSource *input) = 0; + virtual SoundPtr loadRaw(SampleSourcePtr input) = 0; /** @brief Load a sound file from stream. Only valid if canLoadStream @@ -130,7 +130,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual SoundPtr load(Stream::Stream *input) = 0; + virtual SoundPtr load(Stream::StreamPtr input) = 0; /** @brief Load a sound directly from file. Only valid if canLoadFile diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 910690156..99b976bc9 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,6 +1,8 @@ #include "openal_out.h" #include +#include "../../stream/filters/buffer_stream.h" + using namespace Mangle::Sound; // ---- Helper functions and classes ---- @@ -28,7 +30,7 @@ static void checkALError(const std::string &msg) fail("\"" + std::string(alGetString(err)) + "\" while " + msg); } -static void getALFormat(InputStream *inp, int &fmt, int &rate) +static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) { int ch, bits; inp->getInfo(&rate, &ch, &bits); @@ -61,6 +63,41 @@ static void getALFormat(InputStream *inp, int &fmt, int &rate) fail("Unsupported input format"); } +// ---- OpenAL_Factory ---- + +OpenAL_Factory::OpenAL_Factory(bool doSetup) + : didSetup(doSetup) +{ + needsUpdate = false; + has3D = true; + canLoadFile = false; + canLoadStream = false; + canLoadSource = true; + + if(doSetup) + { + // Set up sound system + Device = alcOpenDevice(NULL); + Context = alcCreateContext(Device, NULL); + + if(!Device || !Context) + fail("Failed to initialize context or device"); + + alcMakeContextCurrent(Context); + } +} + +OpenAL_Factory::~OpenAL_Factory() +{ + // Deinitialize sound system + if(didSetup) + { + alcMakeContextCurrent(NULL); + if(Context) alcDestroyContext(Context); + if(Device) alcCloseDevice(Device); + } +} + // ---- OpenAL_Sound ---- void OpenAL_Sound::play() @@ -81,7 +118,7 @@ void OpenAL_Sound::pause() checkALError("pausing"); } -bool OpenAL_Sound::isPlaying() +bool OpenAL_Sound::isPlaying() const { ALint state; alGetSourcei(inst, AL_SOURCE_STATE, &state); @@ -105,7 +142,12 @@ void OpenAL_Sound::setPos(float x, float y, float z) void OpenAL_Sound::setRepeat(bool rep) { - alSourcei(Source, AL_LOOPING, rep?AL_TRUE:AL_FALSE); + alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE); +} + +SoundPtr OpenAL_Sound::clone() const +{ + return SoundPtr(new OpenAL_Sound(bufferID, refCnt)); } // Constructor used for cloned sounds @@ -121,11 +163,12 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) alSourcei(inst, AL_BUFFER, bufferID); } +// Constructor used for original (non-cloned) sounds OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) { // Get the format int fmt, rate; - getALFormat(inp, fmt, rate); + getALFormat(input, fmt, rate); // Set up the OpenAL buffer alGenBuffers(1, &bufferID); @@ -135,15 +178,15 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) if(input->hasPtr) { // If so, we can read the data directly from the stream - alBufferData(bufferID, fmt, &input.getPtr(), input.size(), rate); + alBufferData(bufferID, fmt, input->getPtr(), input->size(), rate); } else { // Read the entire stream into a temporary buffer first - BufferStream buf(input); + Mangle::Stream::BufferStream buf(input); // Then copy that into OpenAL - alBufferData(bufferID, fmt, &buf.getPtr(), buf.size(), rate); + alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate); } checkALError("loading sound buffer"); @@ -165,8 +208,8 @@ OpenAL_Sound::~OpenAL_Sound() // Return sound alDeleteSources(1, &inst); - // Decrease the reference - if(--(*refCnt)) + // Decrease the reference counter + if((-- *refCnt) == 0) { // We're the last owner. Delete the buffer and the counter // itself. diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index 115c3c13e..335880cda 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -2,7 +2,6 @@ #define MANGLE_SOUND_OPENAL_OUT_H #include "../output.h" -#include "../../stream/filters/buffer_stream.h" #include #include @@ -34,7 +33,7 @@ class OpenAL_Sound : public Sound void setPos(float x, float y, float z); void setRepeat(bool); void setStreaming(bool) {} // Not implemented yet - Sound* clone() const; + SoundPtr clone() const; /// Not implemented void setPan(float) {} @@ -50,48 +49,18 @@ class OpenAL_Factory : public SoundFactory /// Initialize object. Pass true (default) if you want the /// constructor to set up the current ALCdevice and ALCcontext for /// you. - OpenAL_Factory(bool doSetup = true) - : didSetup(doSetup) - { - needsUpdate = false; - has3D = true; - canLoadFile = false; - canLoadStream = false; - canLoadSource = true; - - if(doSetup) - { - // Set up sound system - Device = alcOpenDevice(NULL); - Context = alcCreateContext(Device, NULL); - - if(!Device || !Context) - fail("Failed to initialize context or device"); - - alcMakeContextCurrent(Context); - } - } - - ~OpenAL_Factory() - { - // Deinitialize sound system - if(didSetup) - { - alcMakeContextCurrent(NULL); - if(Context) alcDestroyContext(Context); - if(Device) alcCloseDevice(Device); - } - } + OpenAL_Factory(bool doSetup = true); + ~OpenAL_Factory(); - SoundPtr load(const std::string &file, bool stream=false) { assert(0); } - SoundPtr load(Stream::StreamPtr input, bool stream=false) { assert(0); } - SoundPtr load(SampleSourcePtr input, bool stream=false) + SoundPtr load(const std::string &file) { assert(0); } + SoundPtr load(Stream::StreamPtr input) { assert(0); } + SoundPtr loadRaw(SampleSourcePtr input) { return SoundPtr(new OpenAL_Sound(input)); } void update() {} - setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) + void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) { ALfloat orient[6]; orient[0] = fx; diff --git a/sound/source.h b/sound/source.h index f987dfe4f..d5468b518 100644 --- a/sound/source.h +++ b/sound/source.h @@ -21,13 +21,13 @@ class SampleSource : public Stream::Stream /// Get the sample rate, number of channels, and bits per /// sample. NULL parameters are ignored. - virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) const = 0; + virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0; bool eof() const { return isEof; } // Disabled functions by default. You can still override them in // subclasses. - void seek(size_t pos) const { assert(0); } + void seek(size_t pos) { assert(0); } size_t tell() const { assert(0); } size_t size() const { assert(0); } }; diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 1a0dfe8dc..8da4fe3c7 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -2,6 +2,8 @@ #include "../../stream/clients/audiere_file.h" +using namespace Mangle::Stream; + // Exception handling class Audiere_Exception : public std::exception { @@ -109,11 +111,11 @@ AudiereSource::AudiereSource(const std::string &file) setup(); } -AudiereSource::AudiereSource(Stream::StreamPtr input) +AudiereSource::AudiereSource(StreamPtr input) { // Use our Stream::AudiereFile implementation to convert a Mangle // 'Stream' to an Audiere 'File' - sample = OpenSampleSource(new Stream::AudiereFile(input)); + sample = OpenSampleSource(new AudiereFile(input)); if(!sample) fail("Couldn't load stream"); @@ -125,7 +127,7 @@ AudiereSource::AudiereSource(audiere::SampleSourcePtr src) { assert(sample); setup(); } // Common function called from all constructors -AudiereSource::setup() +void AudiereSource::setup() { assert(sample); diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h index 671595a1a..e2d09d006 100644 --- a/sound/sources/audiere_source.h +++ b/sound/sources/audiere_source.h @@ -26,14 +26,14 @@ class AudiereSource : public SampleSource // How much of the above buffer is in use int pullSize; - void getFormat(); + void setup(); public: /// Decode the given sound file AudiereSource(const std::string &file); /// Decode the given sound stream - AudiereSource(Stream::StreamPtr src); + AudiereSource(Mangle::Stream::StreamPtr src); /// Read directly from an existing audiere::SampleSource AudiereSource(audiere::SampleSourcePtr src); @@ -41,9 +41,9 @@ class AudiereSource : public SampleSource void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); size_t read(void *data, size_t length); - void seek(size_t pos) const { sample->setPosition(pos); } - size_t tell() const { return sample->getPosition(); } - size_t size() const { return sample->getLength(); } + void seek(size_t pos) { sample->setPosition(pos/frameSize); } + size_t tell() const { return sample->getPosition()*frameSize; } + size_t size() const { return sample->getLength()*frameSize; } }; #include "loadertemplate.h" diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h index f1ebea2d5..a27a77d10 100644 --- a/sound/sources/loadertemplate.h +++ b/sound/sources/loadertemplate.h @@ -4,22 +4,24 @@ template class SSL_Template : public SampleSourceLoader { + public: + SSL_Template() { canLoadStream = stream; canLoadFile = file; } - SampleSource *load(const std::string &file) + SampleSourcePtr load(const std::string &filename) { assert(canLoadFile); - return new SourceT(file); + return SampleSourcePtr(new SourceT(filename)); } - SampleSource *load(Stream::StreamPtr input) + SampleSourcePtr load(Stream::StreamPtr input) { assert(canLoadStream); - return new SourceT(input); + return SampleSourcePtr(new SourceT(input)); } }; diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 8a9c74812..ca7a933c6 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,19 +1,13 @@ GCC=g++ -I../ -all: audiere_test ffmpeg_openal_test openal_audiere_test +all: openal_audiere_test -L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +#L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -ffmpeg_openal_test: ffmpeg_openal_test.cpp ../servers/input_ffmpeg.cpp ../servers/output_openal.cpp - $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) - -openal_audiere_test: openal_audiere_test.cpp ../servers/input_audiere.cpp ../servers/output_openal.cpp ../../stream/clients/audiere_file.cpp +openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -audiere_test: audiere_test.cpp ../servers/audiere_imp.cpp - $(GCC) $^ -o $@ $(L_AUDIERE) - clean: rm *_test diff --git a/sound/tests/common.cpp b/sound/tests/common.cpp deleted file mode 100644 index 818d1645f..000000000 --- a/sound/tests/common.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// This file is included directly into the test programs - -#include -#include -#include - -using namespace std; - -class TestStream : public Mangle::Stream::Stream -{ - ifstream io; - -public: - - TestStream(const char* name) - { - io.open(name, ios::binary); - isSeekable = true; - hasPosition = true; - hasSize = false; - } - - size_t read(void* buf, size_t len) - { - io.read((char*)buf, len); - return io.gcount(); - } - - void seek(size_t pos) - { - io.seekg(pos); - } - - size_t tell() const - { return ((TestStream*)this)->io.tellg(); } - - size_t size() const - { return 0; } - - bool eof() const - { return io.eof(); } -}; - -void play(const char* name, bool music=false, bool stream=false) -{ - // Only load streams if the backend supports it - if(stream && !mg.canLoadStream) - return; - - cout << "Playing " << name; - if(stream) cout << " (from stream)"; - cout << "\n"; - - Sound *snd = NULL; - Instance *s = NULL; - - try - { - if(stream) - snd = mg.load(new TestStream(name), music); - else - snd = mg.load(name, music); - - - s = snd->getInstance(false, false); - s->play(); - - while(s->isPlaying()) - { - usleep(10000); - if(mg.needsUpdate) mg.update(); - } - } - catch(exception &e) - { - cout << " ERROR: " << e.what() << "\n"; - } - - if(s) s->drop(); - if(snd) snd->drop(); -} - -int main() -{ - play("cow.wav"); - play("owl.ogg", true); - play("cow.wav", false, true); - return 0; -} diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp index 3036e02a2..a39ae3d98 100644 --- a/sound/tests/openal_audiere_test.cpp +++ b/sound/tests/openal_audiere_test.cpp @@ -1,7 +1,54 @@ +#include +#include +#include + +#include "../../stream/servers/file_stream.h" +#include "../../stream/filters/buffer_stream.h" #include "../filters/openal_audiere.h" +using namespace std; +using namespace Mangle::Stream; using namespace Mangle::Sound; OpenAL_Audiere_Factory mg; -#include "common.cpp" +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main() +{ + play("cow.wav"); + play("owl.ogg"); + play("cow.wav", true); + return 0; +} diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.h index f04411f98..d1af09a8c 100644 --- a/stream/filters/buffer_stream.h +++ b/stream/filters/buffer_stream.h @@ -10,6 +10,7 @@ namespace Stream { /** A Stream that reads another Stream into a buffer, and serves it as a MemoryStream. Might be expanded with other capabilities later. */ + class BufferStream : public MemoryStream { std::vector buffer; @@ -17,6 +18,8 @@ class BufferStream : public MemoryStream public: BufferStream(StreamPtr input) { + assert(input); + // Allocate memory, read the stream into it. Then call set() if(input->hasSize) { From f8d3a35cf878fbfbb9ef1f56e1c4e6143737cdef Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 19:48:04 +0100 Subject: [PATCH 037/111] Made separate tests for audiere and openal. Fixed segfault. Everything is peachy. --- sound/outputs/openal_out.cpp | 8 +++- sound/outputs/openal_out.h | 7 ++- sound/sources/audiere_source.cpp | 2 + sound/sources/stream_source.h | 6 +-- sound/tests/Makefile | 8 +++- sound/tests/audiere_source_test.cpp | 69 ++++++++++++++++++++++++++++ sound/tests/cow.raw | Bin 0 -> 37502 bytes sound/tests/openal_audiere_test.cpp | 2 - sound/tests/openal_output_test.cpp | 46 +++++++++++++++++++ 9 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 sound/tests/audiere_source_test.cpp create mode 100644 sound/tests/cow.raw create mode 100644 sound/tests/openal_output_test.cpp diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 99b976bc9..e6d0b3ae7 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -27,7 +27,13 @@ static void checkALError(const std::string &msg) { ALenum err = alGetError(); if(err != AL_NO_ERROR) - fail("\"" + std::string(alGetString(err)) + "\" while " + msg); + { + const ALchar* errmsg = alGetString(err); + if(errmsg) + fail("\"" + std::string(alGetString(err)) + "\" while " + msg); + else + fail("non-specified error while " + msg + " (did you forget to initialize OpenAL?)"); + } } static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index 335880cda..2ec593665 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -21,8 +21,13 @@ class OpenAL_Sound : public Sound int *refCnt; public: + /// Read samples from the given input buffer OpenAL_Sound(SampleSourcePtr input); - OpenAL_Sound(ALuint buf, int *ref); // Used for cloning + + /// Play an existing buffer, with a given ref counter. Used + /// internally for cloning. + OpenAL_Sound(ALuint buf, int *ref); + ~OpenAL_Sound(); void play(); diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 8da4fe3c7..8a6b57ada 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -135,6 +135,8 @@ void AudiereSource::setup() int channels, rate; sample->getFormat(channels, rate, fmt); + pullSize = 0; + // Calculate the size of one frame frameSize = GetSampleSize(fmt) * channels; diff --git a/sound/sources/stream_source.h b/sound/sources/stream_source.h index 278f701e8..42791ccdf 100644 --- a/sound/sources/stream_source.h +++ b/sound/sources/stream_source.h @@ -10,10 +10,10 @@ namespace Sound { class Stream2Samples : public SampleSource { int32_t rate, channels, bits; - Stream::StreamPtr inp; + Mangle::Stream::StreamPtr inp; public: - Stream2Samples(Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits) + Stream2Samples(Mangle::Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits) : inp(_inp), rate(_rate), channels(_channels), bits(_bits) { isSeekable = inp->isSeekable; @@ -24,7 +24,7 @@ class Stream2Samples : public SampleSource /// Get the sample rate, number of channels, and bits per /// sample. NULL parameters are ignored. - void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) const + void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) { if(_rate) *_rate = rate; if(_channels) *_channels = channels; diff --git a/sound/tests/Makefile b/sound/tests/Makefile index ca7a933c6..75fb1e9e6 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -all: openal_audiere_test +all: audiere_source_test openal_output_test openal_audiere_test #L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) @@ -9,5 +9,11 @@ L_AUDIERE=-laudiere openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) +openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ $(L_OPENAL) + +audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp + $(GCC) $^ -o $@ $(L_AUDIERE) + clean: rm *_test diff --git a/sound/tests/audiere_source_test.cpp b/sound/tests/audiere_source_test.cpp new file mode 100644 index 000000000..6dba37b51 --- /dev/null +++ b/sound/tests/audiere_source_test.cpp @@ -0,0 +1,69 @@ +#include + +#include "../../stream/servers/file_stream.h" +#include "../sources/audiere_source.h" +#include "../../stream/filters/buffer_stream.h" + +#include +#include + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +// Contents and size of cow.raw +void *orig; +size_t orig_size; + +void run(SampleSourcePtr &src) +{ + size_t ss = src->size(); + assert(ss == orig_size); + + cout << "Source size: " << ss << endl; + int rate, channels, bits; + src->getInfo(&rate, &channels, &bits); + cout << "rate=" << rate << "\nchannels=" << channels + << "\nbits=" << bits << endl; + + cout << "Reading entire buffer into memory\n"; + void *buf = malloc(ss); + src->read(buf, ss); + + cout << "Comparing...\n"; + if(memcmp(buf, orig, ss) != 0) + { + cout << "Oops!\n"; + assert(0); + } + + cout << "Done\n"; +} + +int main() +{ + { + cout << "Reading cow.raw first\n"; + FileStream tmp("cow.raw"); + orig_size = tmp.size(); + cout << "Size: " << orig_size << endl; + orig = malloc(orig_size); + tmp.read(orig, orig_size); + cout << "Done\n"; + } + + { + cout << "\nLoading cow.wav by filename:\n"; + SampleSourcePtr cow_file( new AudiereSource("cow.wav") ); + run(cow_file); + } + + { + cout << "\nLoading cow.wav by stream:\n"; + StreamPtr inp( new FileStream("cow.wav") ); + SampleSourcePtr cow_stream( new AudiereSource(inp) ); + run(cow_stream); + } + + return 0; +} diff --git a/sound/tests/cow.raw b/sound/tests/cow.raw new file mode 100644 index 0000000000000000000000000000000000000000..c4d155bbfb1caba2e41b1a97e01f29276c945c07 GIT binary patch literal 37502 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 F{|CY68e9MX literal 0 HcmV?d00001 diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp index a39ae3d98..9c49356a2 100644 --- a/sound/tests/openal_audiere_test.cpp +++ b/sound/tests/openal_audiere_test.cpp @@ -1,9 +1,7 @@ #include -#include #include #include "../../stream/servers/file_stream.h" -#include "../../stream/filters/buffer_stream.h" #include "../filters/openal_audiere.h" using namespace std; diff --git a/sound/tests/openal_output_test.cpp b/sound/tests/openal_output_test.cpp new file mode 100644 index 000000000..11b2fc42e --- /dev/null +++ b/sound/tests/openal_output_test.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include "../../stream/servers/file_stream.h" +#include "../sources/stream_source.h" +#include "../outputs/openal_out.h" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +int main() +{ + cout << "Loading cow.raw\n"; + + int rate = 11025; + int chan = 1; + int bits = 16; + + cout << " rate=" << rate << "\n channels=" << chan + << "\n bits=" << bits << endl; + + StreamPtr file( new FileStream("cow.raw") ); + SampleSourcePtr source( new Stream2Samples( file, rate, chan, bits)); + + cout << "Playing\n"; + + // This initializes OpenAL for us, and serves no other purpose. + OpenAL_Factory mg; + + OpenAL_Sound snd(source); + try + { + snd.play(); + + while(snd.isPlaying()) + { + usleep(10000); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } + return 0; +} From fbb77478c0005185e667a495495ceb57f3abf866 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 19:52:37 +0100 Subject: [PATCH 038/111] Updated vfs+sound test in root/tests/ --- tests/Makefile | 2 +- tests/ogrevfs_audiere_openal_test.cpp | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index ed680f3db..d912c0784 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,7 @@ L_OGRE=$(shell pkg-config --libs OGRE) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/servers/input_audiere.cpp ../sound/servers/output_openal.cpp ../stream/clients/audiere_file.cpp +ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/sources/audiere_source.cpp ../sound/outputs/openal_out.cpp ../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) clean: diff --git a/tests/ogrevfs_audiere_openal_test.cpp b/tests/ogrevfs_audiere_openal_test.cpp index 810fd635e..3e267c98a 100644 --- a/tests/ogrevfs_audiere_openal_test.cpp +++ b/tests/ogrevfs_audiere_openal_test.cpp @@ -7,7 +7,7 @@ */ -#include "sound/servers/openal_audiere.h" +#include "sound/filters/openal_audiere.h" #include "vfs/servers/ogre_vfs.h" #include #include @@ -32,21 +32,18 @@ int main() // Ogre file system VFS::OgreVFS vfs; - Sound::OpenAL_Audiere_Manager mg; - Sound::Sound *snd = mg.load(vfs.open("owl.ogg")); + // The main sound system + Sound::OpenAL_Audiere_Factory mg; + Sound::SoundPtr snd = mg.load(vfs.open("owl.ogg")); - Sound::Instance *s = snd->getInstance(false, false); cout << "Playing 'owl.ogg' from 'sound.zip'\n"; - s->play(); + snd->play(); - while(s->isPlaying()) + while(snd->isPlaying()) { usleep(10000); if(mg.needsUpdate) mg.update(); } - if(s) s->drop(); - if(snd) snd->drop(); - return 0; } From cdca68a36800e02d99fe23fc81a075c4c2177865 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 20:18:11 +0100 Subject: [PATCH 039/111] Fixed up FFMpeg input +test --- sound/sources/ffmpeg_source.cpp | 5 +-- sound/sources/ffmpeg_source.h | 7 ++-- sound/tests/Makefile | 7 +++- sound/tests/audiere_source_test.cpp | 1 - sound/tests/ffmpeg_source_test.cpp | 62 +++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 sound/tests/ffmpeg_source_test.cpp diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index af785eaa3..372d1766d 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,4 +1,5 @@ -#include "input_ffmpeg.h" +#include "ffmpeg_source.h" +#include using namespace Mangle::Sound; @@ -52,8 +53,6 @@ FFMpegSource::FFMpegSource(const std::string &file) std::string msg; AVCodec *codec; - empty = false; - if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) fail("Error loading audio file " + file); diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h index d185098fd..2b33a3222 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.h @@ -1,8 +1,7 @@ #ifndef MANGLE_SOUND_FFMPEG_H #define MANGLE_SOUND_FFMPEG_H -#include "../input.h" -#include +#include "../source.h" #include #include @@ -28,7 +27,7 @@ class FFMpegSource : public SampleSource FFMpegSource(const std::string &file); /// Decode the given sound stream (not supported by FFmpeg) - FFMpegSource(Stream::StreamPtr src) { assert(0); } + FFMpegSource(Mangle::Stream::StreamPtr src) { assert(0); } ~FFMpegSource(); @@ -40,7 +39,7 @@ class FFMpegSource : public SampleSource #include "loadertemplate.h" /// A factory that loads FFMpegSources from file -class FFMpegLoader : public SSL_Template +class FFMpegLoader : public SSL_Template { public: diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 75fb1e9e6..79c2ecaf8 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,8 +1,8 @@ GCC=g++ -I../ -all: audiere_source_test openal_output_test openal_audiere_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test -#L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere @@ -15,5 +15,8 @@ openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) +ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp + $(GCC) $^ -o $@ $(L_FFMPEG) + clean: rm *_test diff --git a/sound/tests/audiere_source_test.cpp b/sound/tests/audiere_source_test.cpp index 6dba37b51..1631e89cb 100644 --- a/sound/tests/audiere_source_test.cpp +++ b/sound/tests/audiere_source_test.cpp @@ -2,7 +2,6 @@ #include "../../stream/servers/file_stream.h" #include "../sources/audiere_source.h" -#include "../../stream/filters/buffer_stream.h" #include #include diff --git a/sound/tests/ffmpeg_source_test.cpp b/sound/tests/ffmpeg_source_test.cpp new file mode 100644 index 000000000..294565fb1 --- /dev/null +++ b/sound/tests/ffmpeg_source_test.cpp @@ -0,0 +1,62 @@ +#include + +#include "../../stream/servers/file_stream.h" +#include "../sources/ffmpeg_source.h" + +#include +#include + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +// Contents and size of cow.raw +void *orig; +size_t orig_size; + +void run(SampleSourcePtr &src) +{ + int rate, channels, bits; + src->getInfo(&rate, &channels, &bits); + cout << "rate=" << rate << "\nchannels=" << channels + << "\nbits=" << bits << endl; + + cout << "Reading entire buffer into memory\n"; + void *buf = malloc(orig_size); + size_t ss = src->read(buf, orig_size); + cout << "Actually read: " << ss << endl; + assert(ss == orig_size); + + cout << "Comparing...\n"; + if(memcmp(buf, orig, ss) != 0) + { + cout << "Oops!\n"; + assert(0); + } + + cout << "Done\n"; +} + +int main() +{ + { + cout << "Reading cow.raw first\n"; + FileStream tmp("cow.raw"); + orig_size = tmp.size(); + cout << "Size: " << orig_size << endl; + orig = malloc(orig_size); + tmp.read(orig, orig_size); + cout << "Done\n"; + } + + // Initializes the library, not used for anything else. + FFMpegLoader fm; + + { + cout << "\nLoading cow.wav by filename:\n"; + SampleSourcePtr cow_file( new FFMpegSource("cow.wav") ); + run(cow_file); + } + + return 0; +} From 63a3ad5e905a951ce40a698a933981d06ecee5f9 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 20:41:19 +0100 Subject: [PATCH 040/111] minor Readme work --- README.txt | 86 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/README.txt b/README.txt index 2052d7d7b..0fdc19b05 100644 --- a/README.txt +++ b/README.txt @@ -8,40 +8,47 @@ Documentation: http://asm-soft.com/mangle/docs -Mangle stands for Minimal Abstraction Game Layer, and it's meant to -become a small set of generic interfaces for various game middleware -libraries, such as sound, input, graphics, and so on. It consists of -several independent modules, one for each of these areas. These may be -used together to build an entire game engine, or they can be used +Mangle is the project name for a small set of generic interfaces for +various game middleware libraries, such as sound, input, graphics, and +so on. You can imagine that it stands for "Minimal Abstraction Game +Layer", if you like. It will consist of several more or less +independent modules, one for each of these areas. These may be used +together to build an entire game engine, or they can be used individually as separate libraries. However, Mangle does NOT actually implement a game engine, or any new fundamental functionality. More on that below. -Currently there's modules for sound and streams / archives (file -access). More will come in the future (including input, 2D/3D +Currently there's modules for sound and streams / archives (virtual +file systems.) More will come in the future (including input, 2D/3D graphics, GUI, physics, and more.) Main idea --------- -The idea behind to provide a uniform, consistent interface to other -game libraries. The library does not provide ANY functionality on its -own. Instead it connects to a backend implementation of your choice. +The idea behind Mangle is to provide a uniform, consistent interface +to other game libraries. The library does not provide ANY +functionality on its own. Instead it connects to a backend +implementation of your choice (or of your making.) The Sound module, for example, currently has backends for OpenAL (output only), FFmpeg (input only) and for Audiere. Hopefully we'll -soon add IrrKlang, FMod, DirectSound and Miles to that. It can combine -libraries to get more complete functionality (like using OpenAL for -output and FFmpeg to decode sound files), and it's also easy to write -your own backend if you're using a different (or home-brewed) sound -system. - -Regardless of what backend you use, the front-end interface (found in -sound/sound.h) is identical, and as a library user you shouldn't -notice much difference at all if you swap one backend for another at a -later point. +add IrrKlang, FMod, DirectSound, Miles and more in the future It can +combine libraries to get more complete functionality (like using +OpenAL for output and FFmpeg to decode sound files), and it's also +easy to write your own backend if you're using a different (or +home-brewed) sound system. + +Regardless of what backend you use, the front-end interfaces (found +eg. in sound/output.h) is identical, and as a library user you +shouldn't notice much difference at all if you swap one backend for +another at a later point. It should Just Work. + +The interfaces themselves are also quite simple. Setting up a sound +stream from FFmpeg or other decoder into OpenAL can be quite hairy - +but with Mangle the hairy parts have already been written for you. You +just plug the parts together. The goal in the long run is to support a wide variety of game-related libraries, and as many backend libraries (free and commercial) as @@ -66,24 +73,27 @@ you in many ways: The Mangle interfaces can help you keep your code clean, and its user interface is often simpler than the exteral library one. +- If you want to quickly connect different libraries together, it + really helps if they have speak a common language. The Mangle + interfaces are exactly that. Need to load Audiere sounds from a + weird archive format only implemented for PhysFS, all channeled + through the OGRE resource system? No problem! + - If you are creating a library that depends on a specific feature (such as sound), but you don't want to lock your users into any specific sound library. Mangle works as an abstraction that lets - your users select their own implementation. My own Monster scripting - language ( http://monsterscript.net ) will use this tactic, to - provide native-but-generic sound, input and GUI support, among other - features. + your users select their own implementation. - If you want to support multiple backends, or make it possible to easily switch backends later. You can select backends at compile - time or even at runtime. Maybe you decide to switch to to a - commercial library at a late stage in development, or you discover - that your favorite backend doesn't work on all the platforms you - want to reach. + time or even at runtime. For example you might want to switch to to + a commercial sound library at a later stage in development, or you + may want to use a different input library on console platforms than + on PC. The Mangle implementations are extremely light-weight - often just one -or two cpp/h pairs. You plug them directly into your program, there's -no separate build step required. +or two cpp/h pairs per module. You can plug them directly into your +program, there's no separate library building step required. Since the library aims to be very modularly put together, you can also, in many cases, just copy-and-paste the parts you need and ignore @@ -94,15 +104,15 @@ come crashing down, because there is no big 'system' to speak of. Past and future --------------- -Mangle started out as a spin-off from OpenMW, another project of mine -( http://openmw.sourceforge.net ). OpenMW is an attempt to recreate -the engine behind the commercial game Morrowind, using only open -source software. +Mangle started out as (and still is) a spin-off from OpenMW, another +project I am personally working on ( http://openmw.sourceforge.net ). +OpenMW is an attempt to recreate the engine behind the commercial game +Morrowind, using only open source software. -The projects are still tightly interlinked, and the will continue to -be until OpenMW is finished. That means that all near-future work on -Mangle for my part will be more or less guided by what OpenMW -needs. But I'll gladly accept external contributions that are not +The projects are still tightly interlinked, and they will continue to +be until OpenMW is finished. Most near-future work on Mangle will be +focused chiefly on OpenMW at the moment. However I will gladly +implement external contributions and suggestions that are not OpenMW-related. From 5e70bc7bd768b9fb6016c02656841d199cbb3759 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 2 Jan 2010 10:24:58 +0100 Subject: [PATCH 041/111] Added semi-automatic test scripts --- sound/tests/output/audiere_source_test.out | 21 ++++++++++++ sound/tests/output/ffmpeg_source_test.out | 12 +++++++ sound/tests/output/openal_audiere_test.out | 3 ++ sound/tests/output/openal_output_test.out | 5 +++ sound/tests/test.sh | 18 ++++++++++ stream/tests/output/audiere_client_test.out | 9 +++++ stream/tests/output/buffer_filter_test.out | 22 ++++++++++++ stream/tests/output/file_server_test.out | 3 ++ stream/tests/output/memory_server_test.out | 23 +++++++++++++ stream/tests/output/ogre_client_test.out | 5 +++ stream/tests/output/slice_filter_test.out | 36 ++++++++++++++++++++ stream/tests/test.sh | 18 ++++++++++ testall.sh | 13 +++++++ tests/output/ogrevfs_audiere_openal_test.out | 1 + tests/test.sh | 18 ++++++++++ vfs/tests/output/dummy_test.out | 31 +++++++++++++++++ vfs/tests/output/ogre_client_test.out | 12 +++++++ vfs/tests/output/ogre_resource_test.out | 20 +++++++++++ vfs/tests/output/ogre_server_test.out | 11 ++++++ vfs/tests/output/physfs_server_test.out | 11 ++++++ vfs/tests/test.sh | 18 ++++++++++ 21 files changed, 310 insertions(+) create mode 100644 sound/tests/output/audiere_source_test.out create mode 100644 sound/tests/output/ffmpeg_source_test.out create mode 100644 sound/tests/output/openal_audiere_test.out create mode 100644 sound/tests/output/openal_output_test.out create mode 100755 sound/tests/test.sh create mode 100644 stream/tests/output/audiere_client_test.out create mode 100644 stream/tests/output/buffer_filter_test.out create mode 100644 stream/tests/output/file_server_test.out create mode 100644 stream/tests/output/memory_server_test.out create mode 100644 stream/tests/output/ogre_client_test.out create mode 100644 stream/tests/output/slice_filter_test.out create mode 100755 stream/tests/test.sh create mode 100755 testall.sh create mode 100644 tests/output/ogrevfs_audiere_openal_test.out create mode 100755 tests/test.sh create mode 100644 vfs/tests/output/dummy_test.out create mode 100644 vfs/tests/output/ogre_client_test.out create mode 100644 vfs/tests/output/ogre_resource_test.out create mode 100644 vfs/tests/output/ogre_server_test.out create mode 100644 vfs/tests/output/physfs_server_test.out create mode 100755 vfs/tests/test.sh diff --git a/sound/tests/output/audiere_source_test.out b/sound/tests/output/audiere_source_test.out new file mode 100644 index 000000000..47a5a9e41 --- /dev/null +++ b/sound/tests/output/audiere_source_test.out @@ -0,0 +1,21 @@ +Reading cow.raw first +Size: 37502 +Done + +Loading cow.wav by filename: +Source size: 37502 +rate=11025 +channels=1 +bits=16 +Reading entire buffer into memory +Comparing... +Done + +Loading cow.wav by stream: +Source size: 37502 +rate=11025 +channels=1 +bits=16 +Reading entire buffer into memory +Comparing... +Done diff --git a/sound/tests/output/ffmpeg_source_test.out b/sound/tests/output/ffmpeg_source_test.out new file mode 100644 index 000000000..1c7d49113 --- /dev/null +++ b/sound/tests/output/ffmpeg_source_test.out @@ -0,0 +1,12 @@ +Reading cow.raw first +Size: 37502 +Done + +Loading cow.wav by filename: +rate=11025 +channels=1 +bits=16 +Reading entire buffer into memory +Actually read: 37502 +Comparing... +Done diff --git a/sound/tests/output/openal_audiere_test.out b/sound/tests/output/openal_audiere_test.out new file mode 100644 index 000000000..4fe01eac2 --- /dev/null +++ b/sound/tests/output/openal_audiere_test.out @@ -0,0 +1,3 @@ +Playing cow.wav +Playing owl.ogg +Playing cow.wav (from stream) diff --git a/sound/tests/output/openal_output_test.out b/sound/tests/output/openal_output_test.out new file mode 100644 index 000000000..04392a72e --- /dev/null +++ b/sound/tests/output/openal_output_test.out @@ -0,0 +1,5 @@ +Loading cow.raw + rate=11025 + channels=1 + bits=16 +Playing diff --git a/sound/tests/test.sh b/sound/tests/test.sh new file mode 100755 index 000000000..b1ca6f1a6 --- /dev/null +++ b/sound/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + $a | diff output/$a.out - + else + echo "Creating $a.out" + $a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/stream/tests/output/audiere_client_test.out b/stream/tests/output/audiere_client_test.out new file mode 100644 index 000000000..2130db9fd --- /dev/null +++ b/stream/tests/output/audiere_client_test.out @@ -0,0 +1,9 @@ +pos=0 +2 bytes: he +pos=2 +pos=4 +3 bytes: o w +pos=6 +pos=7 +last 4 bytes: orld +entire stream: hello world diff --git a/stream/tests/output/buffer_filter_test.out b/stream/tests/output/buffer_filter_test.out new file mode 100644 index 000000000..0ca252f25 --- /dev/null +++ b/stream/tests/output/buffer_filter_test.out @@ -0,0 +1,22 @@ +Size: 11 +Pos: 0 +Seeking... +Pos: 3 +Reading: 4 +Four bytes: lo w +Eof: 0 +Pos: 7 +Seeking again... +Pos: 11 +Eof: 1 +Seek to 6 +Eof: 0 +Pos: 6 +Over-reading: 5 +Result: world +Eof: 1 +Pos: 11 +Finally, reading the entire string: 11 +Result: hello world +Eof: 1 +Pos: 11 diff --git a/stream/tests/output/file_server_test.out b/stream/tests/output/file_server_test.out new file mode 100644 index 000000000..240f066a3 --- /dev/null +++ b/stream/tests/output/file_server_test.out @@ -0,0 +1,3 @@ +pos=0 eof=0 +First 20 bytes: #include "../servers +pos=20 eof=0 diff --git a/stream/tests/output/memory_server_test.out b/stream/tests/output/memory_server_test.out new file mode 100644 index 000000000..7cd9533e7 --- /dev/null +++ b/stream/tests/output/memory_server_test.out @@ -0,0 +1,23 @@ +Size: 12 +Pos: 0 +Seeking... +Pos: 3 +Reading: 4 +Four bytes: lo w +Eof: 0 +Pos: 7 +Seeking again... +Pos: 12 +Eof: 1 +Seek to 6 +Eof: 0 +Pos: 6 +Over-reading: 6 +Result: world +Eof: 1 +Pos: 12 +Finally, reading the entire string: 11 +Result: hello world +Eof: 0 +Pos: 11 +Entire stream from pointer: hello world diff --git a/stream/tests/output/ogre_client_test.out b/stream/tests/output/ogre_client_test.out new file mode 100644 index 000000000..62cd14604 --- /dev/null +++ b/stream/tests/output/ogre_client_test.out @@ -0,0 +1,5 @@ +Name: hello +As string: hello world +pos=11 eof=1 +pos=0 eof=0 +pos=3 eof=0 diff --git a/stream/tests/output/slice_filter_test.out b/stream/tests/output/slice_filter_test.out new file mode 100644 index 000000000..6d84704a7 --- /dev/null +++ b/stream/tests/output/slice_filter_test.out @@ -0,0 +1,36 @@ + +Slice 1: +-------- +Size: 6 +Pos: 0 +Seeking... +Reading 6 bytes +Result: hello +Pos: 6 +Eof: 1 +Seeking: +Pos: 2 +Eof: 0 +Reading 4 bytes +Result: llo +Pos: 6 +Eof: 1 +Entire stream as pointer: hello + +Slice 2: +-------- +Size: 6 +Pos: 0 +Seeking... +Reading 6 bytes +Result: world +Pos: 6 +Eof: 1 +Seeking: +Pos: 2 +Eof: 0 +Reading 4 bytes +Result: rld +Pos: 6 +Eof: 1 +Entire stream as pointer: world diff --git a/stream/tests/test.sh b/stream/tests/test.sh new file mode 100755 index 000000000..b1ca6f1a6 --- /dev/null +++ b/stream/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + $a | diff output/$a.out - + else + echo "Creating $a.out" + $a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/testall.sh b/testall.sh new file mode 100755 index 000000000..b2cc3059d --- /dev/null +++ b/testall.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +function run() +{ + cd "$1/tests/" + ./test.sh + cd ../../ +} + +run stream +run vfs +run sound +run . diff --git a/tests/output/ogrevfs_audiere_openal_test.out b/tests/output/ogrevfs_audiere_openal_test.out new file mode 100644 index 000000000..28ea8a71b --- /dev/null +++ b/tests/output/ogrevfs_audiere_openal_test.out @@ -0,0 +1 @@ +Playing 'owl.ogg' from 'sound.zip' diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 000000000..b1ca6f1a6 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + $a | diff output/$a.out - + else + echo "Creating $a.out" + $a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/vfs/tests/output/dummy_test.out b/vfs/tests/output/dummy_test.out new file mode 100644 index 000000000..61f1fda80 --- /dev/null +++ b/vfs/tests/output/dummy_test.out @@ -0,0 +1,31 @@ +Listing all files: +name: file1 +basename: file1 +isDir: 0 +size: 1 +time: 0 +name: dir/file2 +basename: file2 +isDir: 0 +size: 2 +time: 0 + +Stat for single files: +name: file1 +basename: file1 +isDir: 0 +size: 1 +time: 0 + +name: dir/file2 +basename: file2 +isDir: 0 +size: 2 +time: 0 + +name: dir +basename: dir +isDir: 1 +size: 0 +time: 0 +filesize: 11 diff --git a/vfs/tests/output/ogre_client_test.out b/vfs/tests/output/ogre_client_test.out new file mode 100644 index 000000000..bc10b1e5c --- /dev/null +++ b/vfs/tests/output/ogre_client_test.out @@ -0,0 +1,12 @@ +Case: 1 +Name: dummy +Type: Mangle +All files: + file1 + dir/file2 +Non-recursive: + file1 +Dirs: + dir +filesize: 11 +contents: hello world diff --git a/vfs/tests/output/ogre_resource_test.out b/vfs/tests/output/ogre_resource_test.out new file mode 100644 index 000000000..99b2c91ba --- /dev/null +++ b/vfs/tests/output/ogre_resource_test.out @@ -0,0 +1,20 @@ + +File: Makefile +Size: 814 +First line: GCC=g++ -I../ + +File: ogre_resource_test.cpp +Size: 1437 +First line: #include + +File: bleh +Does not exist + +All source files: + physfs_server_test.cpp + dummy_test.cpp + ogre_resource_test.cpp + server_common.cpp + dummy_vfs.cpp + ogre_client_test.cpp + ogre_server_test.cpp diff --git a/vfs/tests/output/ogre_server_test.out b/vfs/tests/output/ogre_server_test.out new file mode 100644 index 000000000..b00752eae --- /dev/null +++ b/vfs/tests/output/ogre_server_test.out @@ -0,0 +1,11 @@ + +File: Makefile +Size: 814 +First 12 bytes: GCC=g++ -I.. + +File: testfile.txt +Size: 13 +First 12 bytes: hello world! + +File: blah_bleh +File doesn't exist diff --git a/vfs/tests/output/physfs_server_test.out b/vfs/tests/output/physfs_server_test.out new file mode 100644 index 000000000..b00752eae --- /dev/null +++ b/vfs/tests/output/physfs_server_test.out @@ -0,0 +1,11 @@ + +File: Makefile +Size: 814 +First 12 bytes: GCC=g++ -I.. + +File: testfile.txt +Size: 13 +First 12 bytes: hello world! + +File: blah_bleh +File doesn't exist diff --git a/vfs/tests/test.sh b/vfs/tests/test.sh new file mode 100755 index 000000000..b1ca6f1a6 --- /dev/null +++ b/vfs/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + $a | diff output/$a.out - + else + echo "Creating $a.out" + $a > "output/$a.out" + git add "output/$a.out" + fi +done From 86089816a7532724e4d8ab464a60229b1177b23a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 2 Jan 2010 13:06:37 +0100 Subject: [PATCH 042/111] Added tools/str_exception.h and better error handling for StdStream and FileStream --- sound/outputs/openal_out.cpp | 16 ++-------------- sound/sources/audiere_source.cpp | 17 ++--------------- sound/sources/ffmpeg_source.cpp | 27 +++------------------------ stream/servers/file_stream.h | 3 +++ stream/servers/std_stream.h | 18 +++++++++++++++--- tools/str_exception.h | 19 +++++++++++++++++++ 6 files changed, 44 insertions(+), 56 deletions(-) create mode 100644 tools/str_exception.h diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index e6d0b3ae7..f0298feb2 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -2,26 +2,14 @@ #include #include "../../stream/filters/buffer_stream.h" +#include "../../tools/str_exception.h" using namespace Mangle::Sound; // ---- Helper functions and classes ---- -class OpenAL_Exception : public std::exception -{ - std::string msg; - - public: - - OpenAL_Exception(const std::string &m) : msg(m) {} - ~OpenAL_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - static void fail(const std::string &msg) -{ - throw OpenAL_Exception("OpenAL exception: " + msg); -} +{ throw str_exception("OpenAL exception: " + msg); } static void checkALError(const std::string &msg) { diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 8a6b57ada..6a0776402 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -1,25 +1,12 @@ #include "audiere_source.h" #include "../../stream/clients/audiere_file.h" +#include "../../tools/str_exception.h" using namespace Mangle::Stream; -// Exception handling -class Audiere_Exception : public std::exception -{ - std::string msg; - - public: - - Audiere_Exception(const std::string &m) : msg(m) {} - ~Audiere_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - static void fail(const std::string &msg) -{ - throw Audiere_Exception("Audiere exception: " + msg); -} +{ throw str_exception("Audiere exception: " + msg); } using namespace audiere; using namespace Mangle::Sound; diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index 372d1766d..5724689de 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,5 +1,6 @@ #include "ffmpeg_source.h" -#include + +#include "../../tools/str_exception.h" using namespace Mangle::Sound; @@ -7,30 +8,8 @@ using namespace Mangle::Sound; // streams operated from the same thread. static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; -/// FFmpeg exception. -class FFM_Exception : public std::exception -{ - std::string msg; - - public: - - FFM_Exception(const std::string &m); - ~FFM_Exception() throw(); - virtual const char* what() const throw(); -}; - -FFM_Exception::FFM_Exception(const std::string &m) - : msg(m) {} - -const char* FFM_Exception::what() const throw() -{ return msg.c_str(); } - -FFM_Exception::~FFM_Exception() throw() {} - static void fail(const std::string &msg) -{ - throw FFM_Exception("FFMpeg exception: " + msg); -} +{ throw str_exception("FFMpeg exception: " + msg); } // --- Loader --- diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.h index 012cb3a78..4aeb7eeac 100644 --- a/stream/servers/file_stream.h +++ b/stream/servers/file_stream.h @@ -18,6 +18,9 @@ class FileStream : public StdStream : StdStream(&file) { file.open(name.c_str(), std::ios::binary); + + if(file.fail()) + throw str_exception("FileStream: failed to open file " + name); } ~FileStream() { file.close(); } }; diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.h index d4b56a9ae..657d9303b 100644 --- a/stream/servers/std_stream.h +++ b/stream/servers/std_stream.h @@ -3,18 +3,20 @@ #include "../stream.h" #include +#include "../../tools/str_exception.h" namespace Mangle { namespace Stream { /** Simplest wrapper for std::istream. - - TODO: No error checking yet. */ class StdStream : public Stream { std::istream *inf; + static void fail(const std::string &msg) + { throw str_exception("StdStream: " + msg); } + public: StdStream(std::istream *_inf) : inf(_inf) @@ -27,11 +29,17 @@ class StdStream : public Stream size_t read(void* buf, size_t len) { inf->read((char*)buf, len); + if(inf->fail()) + fail("error reading from stream"); return inf->gcount(); } void seek(size_t pos) - { inf->seekg(pos); } + { + inf->seekg(pos); + if(inf->fail()) + fail("seek error"); + } size_t tell() const // Hack around the fact that ifstream->tellg() isn't const @@ -44,6 +52,10 @@ class StdStream : public Stream inf->seekg(0, std::ios::end); size_t res = inf->tellg(); inf->seekg(pos); + + if(inf->fail()) + fail("could not get stream size"); + return res; } diff --git a/tools/str_exception.h b/tools/str_exception.h new file mode 100644 index 000000000..39120a3f9 --- /dev/null +++ b/tools/str_exception.h @@ -0,0 +1,19 @@ +#ifndef __STR_EXCEPTION_H +#define __STR_EXCEPTION_H + +#include +#include + +/// A simple exception that takes and holds a string +class str_exception : public std::exception +{ + std::string msg; + + public: + + str_exception(const std::string &m) : msg(m) {} + ~str_exception() throw() {} + const char* what() const throw() { return msg.c_str(); } +}; + +#endif From 4065ac019982660076a5d3e8e2d964ec69c7bb6a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 4 Jan 2010 11:55:38 +0100 Subject: [PATCH 043/111] Changed definition of Stream::getPtr(size), now moves the position locator by 'size' bytes. --- stream/filters/slice_stream.h | 7 ++++++- stream/servers/memory_stream.h | 10 ++++++++-- stream/stream.h | 9 +++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index d792b57d6..990092be7 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -55,7 +55,12 @@ class SliceStream : public Stream size_t size() const { return length; } const void *getPtr() { return getPtr(0, length); } - const void *getPtr(size_t size) { return getPtr(pos, size); } + const void *getPtr(size_t size) + { + void *ptr = getPtr(pos, size); + seek(pos+size); + return ptr; + } const void *getPtr(size_t pos, size_t size) { // Boundry checks on pos and size. Bounding the size is diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index d77779878..29bc476fc 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -71,9 +71,15 @@ class MemoryStream : public Stream size_t size() const { return length; } bool eof() const { return pos == length; } - /// Get the base pointer to the entire buffer const void *getPtr() { return data; } - const void *getPtr(size_t size) { return ((char*)data)+pos; } + const void *getPtr(size_t size) + { + // This variant of getPtr must move the position pointer + size_t opos = pos; + pos += size; + if(pos > length) pos = length; + return ((char*)data)+opos; + } const void *getPtr(size_t pos, size_t size) { if(pos > length) pos = length; diff --git a/stream/stream.h b/stream/stream.h index 309b0b33a..53e662341 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -60,13 +60,14 @@ class Stream /// memory-based streams where using them would be an optimization. virtual const void *getPtr() { assert(0); } - /// Get a pointer to a memory region of 'size' bytes from the - /// current position. - virtual const void *getPtr(size_t size) { assert(0); } - /// Get a pointer to a memory region of 'size' bytes starting from /// position 'pos' virtual const void *getPtr(size_t pos, size_t size) { assert(0); } + + /// Get a pointer to a memory region of 'size' bytes from the + /// current position. Unlike the two other getPtr variants, this + /// will advance the position past the returned area. + virtual const void *getPtr(size_t size) { assert(0); } }; typedef boost::shared_ptr StreamPtr; From 4d16beef1a586211504d4d510ea791bc65682ad3 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 4 Jan 2010 12:13:47 +0100 Subject: [PATCH 044/111] Added Stream::PureFilter --- stream/filters/pure_filter.h | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 stream/filters/pure_filter.h diff --git a/stream/filters/pure_filter.h b/stream/filters/pure_filter.h new file mode 100644 index 000000000..4ce679d48 --- /dev/null +++ b/stream/filters/pure_filter.h @@ -0,0 +1,39 @@ +#ifndef MANGLE_STREAM_FILTER_H +#define MANGLE_STREAM_FILTER_H + +#include "../stream.h" + +namespace Mangle { +namespace Stream { + +/** A stream that filters another stream with no changes. Intended as + a base class for other filters. + */ +class PureFilter : public Stream +{ + protected: + StreamPtr src; + + public: + PureFilter(StreamPtr _src) + : src(_src) + { + isSeekable = src->isSeekable; + hasPosition = src->hasPosition; + hasSize = src->hasSize; + hasPtr = src->hasPtr; + } + + size_t read(void *buf, size_t count) { return src->read(buf, count); } + void seek(size_t pos) { src->seek(pos); } + size_t tell() const { return src->tell(); } + size_t size() const { return src->size(); } + bool eof() const { return src->eof(); } + const void *getPtr() { return src->getPtr(); } + const void *getPtr(size_t size) { return src->getPtr(size); } + const void *getPtr(size_t pos, size_t size) + { return src->getPtr(pos, size); } +}; + +}} // namespaces +#endif From fb1eec974c63fa435286456f5c82b9d9387ff900 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 4 Jan 2010 12:22:52 +0100 Subject: [PATCH 045/111] Fixed const error in slice_stream.h --- stream/filters/slice_stream.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index 990092be7..ade0df52b 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -57,7 +57,7 @@ class SliceStream : public Stream const void *getPtr() { return getPtr(0, length); } const void *getPtr(size_t size) { - void *ptr = getPtr(pos, size); + const void *ptr = getPtr(pos, size); seek(pos+size); return ptr; } From 2a6ed21351464751245c485f933ed788d6cb0698 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 5 Jan 2010 13:16:05 +0100 Subject: [PATCH 046/111] Fixed incorrect hasPtr in MemoryStream --- stream/servers/memory_stream.h | 1 + 1 file changed, 1 insertion(+) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 29bc476fc..e8c1dfbd5 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -30,6 +30,7 @@ class MemoryStream : public Stream isSeekable = true; hasPosition = true; hasSize = true; + hasPtr = true; } MemoryStream() From 20960ad6846c91917335672be6ac72019a56a891 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 6 Feb 2010 11:41:19 +0100 Subject: [PATCH 047/111] comment spell fix --- stream/servers/memory_stream.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index e8c1dfbd5..f20fe204a 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -9,13 +9,13 @@ namespace Mangle { namespace Stream { // Do this before the class declaration, since the class itself -// depends on it. +// uses it. class MemoryStream; typedef boost::shared_ptr MemoryStreamPtr; /** A Stream wrapping a memory buffer - This will create a fully seekable stream out any pointer/length + This will create a fully seekable stream out of any pointer/length pair you give it. */ class MemoryStream : public Stream From 401fa0079c9045e4550c05ca342bde8775e39264 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 4 Mar 2010 11:12:22 +0100 Subject: [PATCH 048/111] Added return statements to assert-functions in stream.h --- stream/stream.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stream/stream.h b/stream/stream.h index 53e662341..acd001d24 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -58,16 +58,16 @@ class Stream /// Return a pointer to the entire stream. This function (and the /// other getPtr() variants below) should only be implemented for /// memory-based streams where using them would be an optimization. - virtual const void *getPtr() { assert(0); } + virtual const void *getPtr() { assert(0); return NULL; } /// Get a pointer to a memory region of 'size' bytes starting from /// position 'pos' - virtual const void *getPtr(size_t pos, size_t size) { assert(0); } + virtual const void *getPtr(size_t pos, size_t size) { assert(0); return NULL; } /// Get a pointer to a memory region of 'size' bytes from the /// current position. Unlike the two other getPtr variants, this /// will advance the position past the returned area. - virtual const void *getPtr(size_t size) { assert(0); } + virtual const void *getPtr(size_t size) { assert(0); return NULL; } }; typedef boost::shared_ptr StreamPtr; From 52e7570b4fddd868cc0483e0fa2e49c50d5a1334 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 19 May 2010 13:33:18 +0200 Subject: [PATCH 049/111] The tests should run when ./ is not in $PATH, too. (Thanks to Henrik Kretzschmar) --- sound/tests/test.sh | 4 ++-- stream/tests/test.sh | 4 ++-- tests/test.sh | 4 ++-- vfs/tests/test.sh | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/tests/test.sh b/sound/tests/test.sh index b1ca6f1a6..2d07708ad 100755 --- a/sound/tests/test.sh +++ b/sound/tests/test.sh @@ -9,10 +9,10 @@ PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" - $a | diff output/$a.out - + ./$a | diff output/$a.out - else echo "Creating $a.out" - $a > "output/$a.out" + ./$a > "output/$a.out" git add "output/$a.out" fi done diff --git a/stream/tests/test.sh b/stream/tests/test.sh index b1ca6f1a6..2d07708ad 100755 --- a/stream/tests/test.sh +++ b/stream/tests/test.sh @@ -9,10 +9,10 @@ PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" - $a | diff output/$a.out - + ./$a | diff output/$a.out - else echo "Creating $a.out" - $a > "output/$a.out" + ./$a > "output/$a.out" git add "output/$a.out" fi done diff --git a/tests/test.sh b/tests/test.sh index b1ca6f1a6..2d07708ad 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -9,10 +9,10 @@ PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" - $a | diff output/$a.out - + ./$a | diff output/$a.out - else echo "Creating $a.out" - $a > "output/$a.out" + ./$a > "output/$a.out" git add "output/$a.out" fi done diff --git a/vfs/tests/test.sh b/vfs/tests/test.sh index b1ca6f1a6..2d07708ad 100755 --- a/vfs/tests/test.sh +++ b/vfs/tests/test.sh @@ -9,10 +9,10 @@ PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" - $a | diff output/$a.out - + ./$a | diff output/$a.out - else echo "Creating $a.out" - $a > "output/$a.out" + ./$a > "output/$a.out" git add "output/$a.out" fi done From 6b0b7c95f8a40a53b4c26d551d4fb5118deb7e12 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 3 Jun 2010 20:13:27 +0200 Subject: [PATCH 050/111] Renamed all .h to .hpp for consistency --- sound/filters/{input_filter.h => input_filter.hpp} | 2 +- .../filters/{openal_audiere.h => openal_audiere.hpp} | 6 +++--- sound/{output.h => output.hpp} | 4 ++-- sound/outputs/openal_out.cpp | 6 +++--- sound/outputs/{openal_out.h => openal_out.hpp} | 2 +- sound/{source.h => source.hpp} | 2 +- sound/sources/audiere_source.cpp | 6 +++--- .../sources/{audiere_source.h => audiere_source.hpp} | 4 ++-- sound/sources/ffmpeg_source.cpp | 4 ++-- sound/sources/{ffmpeg_source.h => ffmpeg_source.hpp} | 4 ++-- .../sources/{loadertemplate.h => loadertemplate.hpp} | 0 sound/sources/{stream_source.h => stream_source.hpp} | 2 +- sound/tests/audiere_source_test.cpp | 4 ++-- sound/tests/ffmpeg_source_test.cpp | 4 ++-- sound/tests/openal_audiere_test.cpp | 4 ++-- sound/tests/openal_output_test.cpp | 6 +++--- stream/clients/audiere_file.cpp | 2 +- stream/clients/{audiere_file.h => audiere_file.hpp} | 2 +- .../{ogre_datastream.h => ogre_datastream.hpp} | 2 +- .../filters/{buffer_stream.h => buffer_stream.hpp} | 2 +- stream/filters/{pure_filter.h => pure_filter.hpp} | 2 +- stream/filters/{slice_stream.h => slice_stream.hpp} | 2 +- stream/servers/{file_stream.h => file_stream.hpp} | 2 +- .../servers/{memory_stream.h => memory_stream.hpp} | 2 +- .../{ogre_datastream.h => ogre_datastream.hpp} | 0 stream/servers/{phys_stream.h => phys_stream.hpp} | 0 stream/servers/{std_stream.h => std_stream.hpp} | 4 ++-- stream/{stream.h => stream.hpp} | 2 +- stream/tests/Makefile | 12 ++++++------ stream/tests/audiere_client_test.cpp | 4 ++-- stream/tests/buffer_filter_test.cpp | 2 +- stream/tests/file_server_test.cpp | 2 +- stream/tests/memory_server_test.cpp | 2 +- stream/tests/ogre_client_test.cpp | 4 ++-- stream/tests/slice_filter_test.cpp | 4 ++-- testall.sh | 1 + tests/ogrevfs_audiere_openal_test.cpp | 4 ++-- tools/{shared_ptr.h => shared_ptr.hpp} | 0 tools/{str_exception.h => str_exception.hpp} | 0 vfs/clients/ogre_archive.cpp | 4 ++-- vfs/clients/{ogre_archive.h => ogre_archive.hpp} | 2 +- vfs/servers/ogre_vfs.cpp | 4 ++-- vfs/servers/{ogre_vfs.h => ogre_vfs.hpp} | 2 +- vfs/servers/{physfs_vfs.h => physfs_vfs.hpp} | 4 ++-- vfs/tests/Makefile | 8 ++++---- vfs/tests/dummy_vfs.cpp | 4 ++-- vfs/tests/ogre_client_test.cpp | 2 +- vfs/tests/ogre_server_test.cpp | 2 +- vfs/tests/output/ogre_resource_test.out | 2 +- vfs/tests/output/ogre_server_test.out | 2 +- vfs/tests/output/physfs_server_test.out | 2 +- vfs/tests/physfs_server_test.cpp | 2 +- vfs/{vfs.h => vfs.hpp} | 2 +- 53 files changed, 80 insertions(+), 79 deletions(-) rename sound/filters/{input_filter.h => input_filter.hpp} (98%) rename sound/filters/{openal_audiere.h => openal_audiere.hpp} (81%) rename sound/{output.h => output.hpp} (98%) rename sound/outputs/{openal_out.h => openal_out.hpp} (98%) rename sound/{source.h => source.hpp} (97%) rename sound/sources/{audiere_source.h => audiere_source.hpp} (96%) rename sound/sources/{ffmpeg_source.h => ffmpeg_source.hpp} (94%) rename sound/sources/{loadertemplate.h => loadertemplate.hpp} (100%) rename sound/sources/{stream_source.h => stream_source.hpp} (98%) rename stream/clients/{audiere_file.h => audiere_file.hpp} (97%) rename stream/clients/{ogre_datastream.h => ogre_datastream.hpp} (98%) rename stream/filters/{buffer_stream.h => buffer_stream.hpp} (97%) rename stream/filters/{pure_filter.h => pure_filter.hpp} (97%) rename stream/filters/{slice_stream.h => slice_stream.hpp} (98%) rename stream/servers/{file_stream.h => file_stream.hpp} (95%) rename stream/servers/{memory_stream.h => memory_stream.hpp} (99%) rename stream/servers/{ogre_datastream.h => ogre_datastream.hpp} (100%) rename stream/servers/{phys_stream.h => phys_stream.hpp} (100%) rename stream/servers/{std_stream.h => std_stream.hpp} (95%) rename stream/{stream.h => stream.hpp} (98%) rename tools/{shared_ptr.h => shared_ptr.hpp} (100%) rename tools/{str_exception.h => str_exception.hpp} (100%) rename vfs/clients/{ogre_archive.h => ogre_archive.hpp} (98%) rename vfs/servers/{ogre_vfs.h => ogre_vfs.hpp} (99%) rename vfs/servers/{physfs_vfs.h => physfs_vfs.hpp} (96%) rename vfs/{vfs.h => vfs.hpp} (98%) diff --git a/sound/filters/input_filter.h b/sound/filters/input_filter.hpp similarity index 98% rename from sound/filters/input_filter.h rename to sound/filters/input_filter.hpp index 2b4486b75..267858de1 100644 --- a/sound/filters/input_filter.h +++ b/sound/filters/input_filter.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_INPUT_FILTER_H #define MANGLE_INPUT_FILTER_H -#include "../output.h" +#include "../output.hpp" #include diff --git a/sound/filters/openal_audiere.h b/sound/filters/openal_audiere.hpp similarity index 81% rename from sound/filters/openal_audiere.h rename to sound/filters/openal_audiere.hpp index a1b2e6b64..5b62bd11b 100644 --- a/sound/filters/openal_audiere.h +++ b/sound/filters/openal_audiere.hpp @@ -1,9 +1,9 @@ #ifndef MANGLE_FFMPEG_OPENAL_H #define MANGLE_FFMPEG_OPENAL_H -#include "input_filter.h" -#include "../sources/audiere_source.h" -#include "../outputs/openal_out.h" +#include "input_filter.hpp" +#include "../sources/audiere_source.hpp" +#include "../outputs/openal_out.hpp" namespace Mangle { namespace Sound { diff --git a/sound/output.h b/sound/output.hpp similarity index 98% rename from sound/output.h rename to sound/output.hpp index 6bcd93237..2ead277bc 100644 --- a/sound/output.h +++ b/sound/output.hpp @@ -2,9 +2,9 @@ #define MANGLE_SOUND_OUTPUT_H #include -#include "source.h" +#include "source.hpp" -#include "../stream/stream.h" +#include "../stream/stream.hpp" namespace Mangle { namespace Sound { diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index f0298feb2..81e222362 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,8 +1,8 @@ -#include "openal_out.h" +#include "openal_out.hpp" #include -#include "../../stream/filters/buffer_stream.h" -#include "../../tools/str_exception.h" +#include "../../stream/filters/buffer_stream.hpp" +#include "../../tools/str_exception.hpp" using namespace Mangle::Sound; diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.hpp similarity index 98% rename from sound/outputs/openal_out.h rename to sound/outputs/openal_out.hpp index 2ec593665..18f32f2a9 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_OPENAL_OUT_H #define MANGLE_SOUND_OPENAL_OUT_H -#include "../output.h" +#include "../output.hpp" #include #include diff --git a/sound/source.h b/sound/source.hpp similarity index 97% rename from sound/source.h rename to sound/source.hpp index d5468b518..1eb42a283 100644 --- a/sound/source.h +++ b/sound/source.hpp @@ -5,7 +5,7 @@ #include #include -#include "../stream/stream.h" +#include "../stream/stream.hpp" namespace Mangle { namespace Sound { diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 6a0776402..6fde40c07 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -1,7 +1,7 @@ -#include "audiere_source.h" +#include "audiere_source.hpp" -#include "../../stream/clients/audiere_file.h" -#include "../../tools/str_exception.h" +#include "../../stream/clients/audiere_file.hpp" +#include "../../tools/str_exception.hpp" using namespace Mangle::Stream; diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.hpp similarity index 96% rename from sound/sources/audiere_source.h rename to sound/sources/audiere_source.hpp index e2d09d006..476546af3 100644 --- a/sound/sources/audiere_source.h +++ b/sound/sources/audiere_source.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_AUDIERE_SOURCE_H #define MANGLE_SOUND_AUDIERE_SOURCE_H -#include "../source.h" +#include "../source.hpp" #include @@ -46,7 +46,7 @@ class AudiereSource : public SampleSource size_t size() const { return sample->getLength()*frameSize; } }; -#include "loadertemplate.h" +#include "loadertemplate.hpp" /// A factory that loads AudiereSources from file and stream typedef SSL_Template AudiereLoader; diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index 5724689de..bfb428234 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,6 +1,6 @@ -#include "ffmpeg_source.h" +#include "ffmpeg_source.hpp" -#include "../../tools/str_exception.h" +#include "../../tools/str_exception.hpp" using namespace Mangle::Sound; diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.hpp similarity index 94% rename from sound/sources/ffmpeg_source.h rename to sound/sources/ffmpeg_source.hpp index 2b33a3222..f0b5342ac 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_FFMPEG_H #define MANGLE_SOUND_FFMPEG_H -#include "../source.h" +#include "../source.hpp" #include #include @@ -36,7 +36,7 @@ class FFMpegSource : public SampleSource size_t read(void *data, size_t length); }; -#include "loadertemplate.h" +#include "loadertemplate.hpp" /// A factory that loads FFMpegSources from file class FFMpegLoader : public SSL_Template diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.hpp similarity index 100% rename from sound/sources/loadertemplate.h rename to sound/sources/loadertemplate.hpp diff --git a/sound/sources/stream_source.h b/sound/sources/stream_source.hpp similarity index 98% rename from sound/sources/stream_source.h rename to sound/sources/stream_source.hpp index 42791ccdf..e4863545e 100644 --- a/sound/sources/stream_source.h +++ b/sound/sources/stream_source.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_STREAMSOURCE_H #define MANGLE_SOUND_STREAMSOURCE_H -#include "../source.h" +#include "../source.hpp" namespace Mangle { namespace Sound { diff --git a/sound/tests/audiere_source_test.cpp b/sound/tests/audiere_source_test.cpp index 1631e89cb..637d743b2 100644 --- a/sound/tests/audiere_source_test.cpp +++ b/sound/tests/audiere_source_test.cpp @@ -1,7 +1,7 @@ #include -#include "../../stream/servers/file_stream.h" -#include "../sources/audiere_source.h" +#include "../../stream/servers/file_stream.hpp" +#include "../sources/audiere_source.hpp" #include #include diff --git a/sound/tests/ffmpeg_source_test.cpp b/sound/tests/ffmpeg_source_test.cpp index 294565fb1..f03b15b99 100644 --- a/sound/tests/ffmpeg_source_test.cpp +++ b/sound/tests/ffmpeg_source_test.cpp @@ -1,7 +1,7 @@ #include -#include "../../stream/servers/file_stream.h" -#include "../sources/ffmpeg_source.h" +#include "../../stream/servers/file_stream.hpp" +#include "../sources/ffmpeg_source.hpp" #include #include diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp index 9c49356a2..ced7fe5d2 100644 --- a/sound/tests/openal_audiere_test.cpp +++ b/sound/tests/openal_audiere_test.cpp @@ -1,8 +1,8 @@ #include #include -#include "../../stream/servers/file_stream.h" -#include "../filters/openal_audiere.h" +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_audiere.hpp" using namespace std; using namespace Mangle::Stream; diff --git a/sound/tests/openal_output_test.cpp b/sound/tests/openal_output_test.cpp index 11b2fc42e..bc781c1a4 100644 --- a/sound/tests/openal_output_test.cpp +++ b/sound/tests/openal_output_test.cpp @@ -1,9 +1,9 @@ #include #include -#include "../../stream/servers/file_stream.h" -#include "../sources/stream_source.h" -#include "../outputs/openal_out.h" +#include "../../stream/servers/file_stream.hpp" +#include "../sources/stream_source.hpp" +#include "../outputs/openal_out.hpp" using namespace std; using namespace Mangle::Stream; diff --git a/stream/clients/audiere_file.cpp b/stream/clients/audiere_file.cpp index 53638781e..1d8e5b706 100644 --- a/stream/clients/audiere_file.cpp +++ b/stream/clients/audiere_file.cpp @@ -1,4 +1,4 @@ -#include "audiere_file.h" +#include "audiere_file.hpp" using namespace audiere; using namespace Mangle::Stream; diff --git a/stream/clients/audiere_file.h b/stream/clients/audiere_file.hpp similarity index 97% rename from stream/clients/audiere_file.h rename to stream/clients/audiere_file.hpp index 670b36ffa..691525ad9 100644 --- a/stream/clients/audiere_file.h +++ b/stream/clients/audiere_file.hpp @@ -4,7 +4,7 @@ #include #include -#include "../stream.h" +#include "../stream.hpp" namespace Mangle { namespace Stream { diff --git a/stream/clients/ogre_datastream.h b/stream/clients/ogre_datastream.hpp similarity index 98% rename from stream/clients/ogre_datastream.h rename to stream/clients/ogre_datastream.hpp index 5369ed11a..80c37fee7 100644 --- a/stream/clients/ogre_datastream.h +++ b/stream/clients/ogre_datastream.hpp @@ -3,7 +3,7 @@ #include #include -#include "../stream.h" +#include "../stream.hpp" namespace Mangle { namespace Stream { diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.hpp similarity index 97% rename from stream/filters/buffer_stream.h rename to stream/filters/buffer_stream.hpp index d1af09a8c..0d22798d0 100644 --- a/stream/filters/buffer_stream.h +++ b/stream/filters/buffer_stream.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_STREAM_BUFFER_H #define MANGLE_STREAM_BUFFER_H -#include "../servers/memory_stream.h" +#include "../servers/memory_stream.hpp" #include namespace Mangle { diff --git a/stream/filters/pure_filter.h b/stream/filters/pure_filter.hpp similarity index 97% rename from stream/filters/pure_filter.h rename to stream/filters/pure_filter.hpp index 4ce679d48..e4ae24375 100644 --- a/stream/filters/pure_filter.h +++ b/stream/filters/pure_filter.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_STREAM_FILTER_H #define MANGLE_STREAM_FILTER_H -#include "../stream.h" +#include "../stream.hpp" namespace Mangle { namespace Stream { diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.hpp similarity index 98% rename from stream/filters/slice_stream.h rename to stream/filters/slice_stream.hpp index ade0df52b..49f241cfc 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_STREAM_SLICE_H #define MANGLE_STREAM_SLICE_H -#include "../stream.h" +#include "../stream.hpp" namespace Mangle { namespace Stream { diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.hpp similarity index 95% rename from stream/servers/file_stream.h rename to stream/servers/file_stream.hpp index 4aeb7eeac..c789d2022 100644 --- a/stream/servers/file_stream.h +++ b/stream/servers/file_stream.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_STREAM_FILESERVER_H #define MANGLE_STREAM_FILESERVER_H -#include "std_stream.h" +#include "std_stream.hpp" #include namespace Mangle { diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.hpp similarity index 99% rename from stream/servers/memory_stream.h rename to stream/servers/memory_stream.hpp index f20fe204a..0849e4a3c 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.hpp @@ -2,7 +2,7 @@ #define MANGLE_STREAM_MEMSERVER_H #include -#include "../stream.h" +#include "../stream.hpp" #include namespace Mangle { diff --git a/stream/servers/ogre_datastream.h b/stream/servers/ogre_datastream.hpp similarity index 100% rename from stream/servers/ogre_datastream.h rename to stream/servers/ogre_datastream.hpp diff --git a/stream/servers/phys_stream.h b/stream/servers/phys_stream.hpp similarity index 100% rename from stream/servers/phys_stream.h rename to stream/servers/phys_stream.hpp diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.hpp similarity index 95% rename from stream/servers/std_stream.h rename to stream/servers/std_stream.hpp index 657d9303b..bb8bb6cbc 100644 --- a/stream/servers/std_stream.h +++ b/stream/servers/std_stream.hpp @@ -1,9 +1,9 @@ #ifndef MANGLE_STREAM_STDIOSERVER_H #define MANGLE_STREAM_STDIOSERVER_H -#include "../stream.h" +#include "../stream.hpp" #include -#include "../../tools/str_exception.h" +#include "../../tools/str_exception.hpp" namespace Mangle { namespace Stream { diff --git a/stream/stream.h b/stream/stream.hpp similarity index 98% rename from stream/stream.h rename to stream/stream.hpp index acd001d24..a195fe321 100644 --- a/stream/stream.h +++ b/stream/stream.hpp @@ -2,7 +2,7 @@ #define MANGLE_STREAM_INPUT_H #include -#include "../tools/shared_ptr.h" +#include "../tools/shared_ptr.hpp" #include namespace Mangle { diff --git a/stream/tests/Makefile b/stream/tests/Makefile index d95908afb..8504de741 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -6,22 +6,22 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_AUDIERE=-laudiere -ogre_client_test: ogre_client_test.cpp ../stream.h ../clients/ogre_datastream.h +ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream.hpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/audiere_file.h ../clients/audiere_file.cpp +audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -file_server_test: file_server_test.cpp ../stream.h ../servers/file_stream.h ../servers/std_stream.h +file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ -memory_server_test: memory_server_test.cpp ../stream.h ../servers/memory_stream.h +memory_server_test: memory_server_test.cpp ../stream.hpp ../servers/memory_stream.hpp $(GCC) $< -o $@ -buffer_filter_test: buffer_filter_test.cpp ../stream.h ../servers/memory_stream.h ../filters/buffer_stream.h +buffer_filter_test: buffer_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/buffer_stream.hpp $(GCC) $< -o $@ -slice_filter_test: slice_filter_test.cpp ../stream.h ../servers/memory_stream.h ../filters/slice_stream.h +slice_filter_test: slice_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/slice_stream.hpp $(GCC) $< -o $@ clean: diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp index dd4dfe4ad..82be569cc 100644 --- a/stream/tests/audiere_client_test.cpp +++ b/stream/tests/audiere_client_test.cpp @@ -1,5 +1,5 @@ -#include "../servers/memory_stream.h" -#include "../clients/audiere_file.h" +#include "../servers/memory_stream.hpp" +#include "../clients/audiere_file.hpp" #include #include diff --git a/stream/tests/buffer_filter_test.cpp b/stream/tests/buffer_filter_test.cpp index b6f43e99b..971530d84 100644 --- a/stream/tests/buffer_filter_test.cpp +++ b/stream/tests/buffer_filter_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "../filters/buffer_stream.h" +#include "../filters/buffer_stream.hpp" using namespace Mangle::Stream; using namespace std; diff --git a/stream/tests/file_server_test.cpp b/stream/tests/file_server_test.cpp index 1c2505158..3e1e3cfa5 100644 --- a/stream/tests/file_server_test.cpp +++ b/stream/tests/file_server_test.cpp @@ -1,4 +1,4 @@ -#include "../servers/file_stream.h" +#include "../servers/file_stream.hpp" #include using namespace Mangle::Stream; diff --git a/stream/tests/memory_server_test.cpp b/stream/tests/memory_server_test.cpp index b9f21b9b1..24dea79a2 100644 --- a/stream/tests/memory_server_test.cpp +++ b/stream/tests/memory_server_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "../servers/memory_stream.h" +#include "../servers/memory_stream.hpp" using namespace Mangle::Stream; using namespace std; diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp index a89589d7c..c8d0442c0 100644 --- a/stream/tests/ogre_client_test.cpp +++ b/stream/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ -#include "../servers/memory_stream.h" -#include "../clients/ogre_datastream.h" +#include "../servers/memory_stream.hpp" +#include "../clients/ogre_datastream.hpp" #include using namespace Mangle::Stream; diff --git a/stream/tests/slice_filter_test.cpp b/stream/tests/slice_filter_test.cpp index ff384c1bb..da90e24ad 100644 --- a/stream/tests/slice_filter_test.cpp +++ b/stream/tests/slice_filter_test.cpp @@ -1,8 +1,8 @@ #include #include -#include "../filters/slice_stream.h" -#include "../servers/memory_stream.h" +#include "../filters/slice_stream.hpp" +#include "../servers/memory_stream.hpp" using namespace Mangle::Stream; using namespace std; diff --git a/testall.sh b/testall.sh index b2cc3059d..f38939a19 100755 --- a/testall.sh +++ b/testall.sh @@ -2,6 +2,7 @@ function run() { + echo "TESTING $1" cd "$1/tests/" ./test.sh cd ../../ diff --git a/tests/ogrevfs_audiere_openal_test.cpp b/tests/ogrevfs_audiere_openal_test.cpp index 3e267c98a..4936538c5 100644 --- a/tests/ogrevfs_audiere_openal_test.cpp +++ b/tests/ogrevfs_audiere_openal_test.cpp @@ -7,8 +7,8 @@ */ -#include "sound/filters/openal_audiere.h" -#include "vfs/servers/ogre_vfs.h" +#include "sound/filters/openal_audiere.hpp" +#include "vfs/servers/ogre_vfs.hpp" #include #include diff --git a/tools/shared_ptr.h b/tools/shared_ptr.hpp similarity index 100% rename from tools/shared_ptr.h rename to tools/shared_ptr.hpp diff --git a/tools/str_exception.h b/tools/str_exception.hpp similarity index 100% rename from tools/str_exception.h rename to tools/str_exception.hpp diff --git a/vfs/clients/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp index e9612d7d9..ed6337053 100644 --- a/vfs/clients/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -1,6 +1,6 @@ -#include "ogre_archive.h" +#include "ogre_archive.hpp" -#include "../../stream/clients/ogre_datastream.h" +#include "../../stream/clients/ogre_datastream.hpp" using namespace Mangle::VFS; using namespace Mangle::Stream; diff --git a/vfs/clients/ogre_archive.h b/vfs/clients/ogre_archive.hpp similarity index 98% rename from vfs/clients/ogre_archive.h rename to vfs/clients/ogre_archive.hpp index 22964dc35..0ea941366 100644 --- a/vfs/clients/ogre_archive.h +++ b/vfs/clients/ogre_archive.hpp @@ -3,7 +3,7 @@ #include #include -#include "../vfs.h" +#include "../vfs.hpp" namespace Mangle { namespace VFS { diff --git a/vfs/servers/ogre_vfs.cpp b/vfs/servers/ogre_vfs.cpp index ab496a192..7f728c1b0 100644 --- a/vfs/servers/ogre_vfs.cpp +++ b/vfs/servers/ogre_vfs.cpp @@ -1,5 +1,5 @@ -#include "ogre_vfs.h" -#include "../../stream/servers/ogre_datastream.h" +#include "ogre_vfs.hpp" +#include "../../stream/servers/ogre_datastream.hpp" using namespace Mangle::VFS; using namespace Mangle::Stream; diff --git a/vfs/servers/ogre_vfs.h b/vfs/servers/ogre_vfs.hpp similarity index 99% rename from vfs/servers/ogre_vfs.h rename to vfs/servers/ogre_vfs.hpp index f212432f8..7ab64169d 100644 --- a/vfs/servers/ogre_vfs.h +++ b/vfs/servers/ogre_vfs.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_VFS_OGRESERVER_H #define MANGLE_VFS_OGRESERVER_H -#include "../vfs.h" +#include "../vfs.hpp" #include namespace Mangle { diff --git a/vfs/servers/physfs_vfs.h b/vfs/servers/physfs_vfs.hpp similarity index 96% rename from vfs/servers/physfs_vfs.h rename to vfs/servers/physfs_vfs.hpp index 49acc398d..8535088e0 100644 --- a/vfs/servers/physfs_vfs.h +++ b/vfs/servers/physfs_vfs.hpp @@ -1,8 +1,8 @@ #ifndef MANGLE_VFS_PHYSFS_SERVER_H #define MANGLE_VFS_PHYSFS_SERVER_H -#include "../vfs.h" -#include "../../stream/servers/phys_stream.h" +#include "../vfs.hpp" +#include "../../stream/servers/phys_stream.hpp" #include #include diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index c9963cfa1..e0577b64e 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -6,19 +6,19 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_PHYSFS=-lphysfs -ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../clients/ogre_archive.h ../clients/ogre_archive.cpp +ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.hpp ../clients/ogre_archive.hpp ../clients/ogre_archive.cpp $(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) ogre_resource_test: ogre_resource_test.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -ogre_server_test: ogre_server_test.cpp ../vfs.h ../servers/ogre_vfs.h ../servers/ogre_vfs.cpp +ogre_server_test: ogre_server_test.cpp ../vfs.hpp ../servers/ogre_vfs.hpp ../servers/ogre_vfs.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../servers/ogre_vfs.cpp -physfs_server_test: physfs_server_test.cpp ../vfs.h ../servers/physfs_vfs.h +physfs_server_test: physfs_server_test.cpp ../vfs.hpp ../servers/physfs_vfs.hpp $(GCC) $< -o $@ $(L_PHYSFS) -dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h +dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.hpp $(GCC) $< -o $@ clean: diff --git a/vfs/tests/dummy_vfs.cpp b/vfs/tests/dummy_vfs.cpp index 7a39da932..0448e96a5 100644 --- a/vfs/tests/dummy_vfs.cpp +++ b/vfs/tests/dummy_vfs.cpp @@ -1,9 +1,9 @@ // This file is shared between several test programs -#include "vfs.h" +#include "vfs.hpp" #include #include -#include "../../stream/servers/memory_stream.h" +#include "../../stream/servers/memory_stream.hpp" using namespace Mangle::VFS; using namespace Mangle::Stream; diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp index 542ac9c7e..8e1269b56 100644 --- a/vfs/tests/ogre_client_test.cpp +++ b/vfs/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ #include "dummy_vfs.cpp" -#include "../clients/ogre_archive.h" +#include "../clients/ogre_archive.hpp" #include using namespace Ogre; diff --git a/vfs/tests/ogre_server_test.cpp b/vfs/tests/ogre_server_test.cpp index cb4624894..b846eef96 100644 --- a/vfs/tests/ogre_server_test.cpp +++ b/vfs/tests/ogre_server_test.cpp @@ -1,4 +1,4 @@ -#include "../servers/ogre_vfs.h" +#include "../servers/ogre_vfs.hpp" #include diff --git a/vfs/tests/output/ogre_resource_test.out b/vfs/tests/output/ogre_resource_test.out index 99b2c91ba..9bfc4ff5b 100644 --- a/vfs/tests/output/ogre_resource_test.out +++ b/vfs/tests/output/ogre_resource_test.out @@ -1,6 +1,6 @@ File: Makefile -Size: 814 +Size: 828 First line: GCC=g++ -I../ File: ogre_resource_test.cpp diff --git a/vfs/tests/output/ogre_server_test.out b/vfs/tests/output/ogre_server_test.out index b00752eae..74f350844 100644 --- a/vfs/tests/output/ogre_server_test.out +++ b/vfs/tests/output/ogre_server_test.out @@ -1,6 +1,6 @@ File: Makefile -Size: 814 +Size: 828 First 12 bytes: GCC=g++ -I.. File: testfile.txt diff --git a/vfs/tests/output/physfs_server_test.out b/vfs/tests/output/physfs_server_test.out index b00752eae..74f350844 100644 --- a/vfs/tests/output/physfs_server_test.out +++ b/vfs/tests/output/physfs_server_test.out @@ -1,6 +1,6 @@ File: Makefile -Size: 814 +Size: 828 First 12 bytes: GCC=g++ -I.. File: testfile.txt diff --git a/vfs/tests/physfs_server_test.cpp b/vfs/tests/physfs_server_test.cpp index ae42083fd..9e9d088cd 100644 --- a/vfs/tests/physfs_server_test.cpp +++ b/vfs/tests/physfs_server_test.cpp @@ -1,4 +1,4 @@ -#include "../servers/physfs_vfs.h" +#include "../servers/physfs_vfs.hpp" #include "server_common.cpp" diff --git a/vfs/vfs.h b/vfs/vfs.hpp similarity index 98% rename from vfs/vfs.h rename to vfs/vfs.hpp index c52bcaebb..ce51be94b 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_VFS_H #define MANGLE_VFS_H -#include "../stream/stream.h" +#include "../stream/stream.hpp" #include #include From 19649bfeaa5a204cc765c7b0a66789919d6681d9 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 25 Jun 2010 15:20:04 +0200 Subject: [PATCH 051/111] Minor -Wall warning fixes, expanded a comment --- stream/clients/ogre_datastream.hpp | 2 +- stream/filters/buffer_stream.hpp | 2 +- tools/str_exception.hpp | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/stream/clients/ogre_datastream.hpp b/stream/clients/ogre_datastream.hpp index 80c37fee7..7b4106001 100644 --- a/stream/clients/ogre_datastream.hpp +++ b/stream/clients/ogre_datastream.hpp @@ -32,7 +32,7 @@ class Mangle2OgreStream : public Ogre::DataStream /// Constructor for a named data stream Mangle2OgreStream(const Ogre::String &name, StreamPtr _inp) - : inp(_inp), Ogre::DataStream(name) { init(); } + : Ogre::DataStream(name), inp(_inp) { init(); } // Only implement the DataStream functions we have to implement diff --git a/stream/filters/buffer_stream.hpp b/stream/filters/buffer_stream.hpp index 0d22798d0..63b70000e 100644 --- a/stream/filters/buffer_stream.hpp +++ b/stream/filters/buffer_stream.hpp @@ -37,7 +37,7 @@ class BufferStream : public MemoryStream { // We DON'T know how big the stream is. We'll have to read // it in increments. - const int ADD = 32*1024; + const unsigned int ADD = 32*1024; size_t len=0, newlen; while(!input->eof()) diff --git a/tools/str_exception.hpp b/tools/str_exception.hpp index 39120a3f9..269501055 100644 --- a/tools/str_exception.hpp +++ b/tools/str_exception.hpp @@ -4,7 +4,13 @@ #include #include -/// A simple exception that takes and holds a string +/** @brief A simple exception that takes and holds a string + + Usage: + + throw str_exception("message"); + + */ class str_exception : public std::exception { std::string msg; From eed875ae043c3c48fd23e18342c17d6f2331cc17 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 25 Jun 2010 16:00:07 +0200 Subject: [PATCH 052/111] readme update --- README.txt | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/README.txt b/README.txt index 0fdc19b05..f4849bebd 100644 --- a/README.txt +++ b/README.txt @@ -34,7 +34,7 @@ implementation of your choice (or of your making.) The Sound module, for example, currently has backends for OpenAL (output only), FFmpeg (input only) and for Audiere. Hopefully we'll -add IrrKlang, FMod, DirectSound, Miles and more in the future It can +add IrrKlang, FMod, DirectSound, Miles and more in the future. It can combine libraries to get more complete functionality (like using OpenAL for output and FFmpeg to decode sound files), and it's also easy to write your own backend if you're using a different (or @@ -74,22 +74,23 @@ you in many ways: user interface is often simpler than the exteral library one. - If you want to quickly connect different libraries together, it - really helps if they have speak a common language. The Mangle - interfaces are exactly that. Need to load Audiere sounds from a - weird archive format only implemented for PhysFS, all channeled - through the OGRE resource system? No problem! + really helps if they speak a common language. The Mangle interfaces + are exactly that - a common language between libraries. Do you need + Audiere to load sounds from a weird archive format only implemented + for PhysFS, all channeled through the OGRE resource system? No + problem! - If you are creating a library that depends on a specific feature (such as sound), but you don't want to lock your users into any specific sound library. Mangle works as an abstraction that lets your users select their own implementation. -- If you want to support multiple backends, or make it possible to - easily switch backends later. You can select backends at compile - time or even at runtime. For example you might want to switch to to - a commercial sound library at a later stage in development, or you - may want to use a different input library on console platforms than - on PC. +- If you want to support multiple backends for your game/app, or want + to make it possible to easily switch backends later. You can select + backends at compile time or even at runtime. For example you might + want to switch to to a commercial sound library at a later stage in + development, or you may want to use a different input library on + console platforms than on PC. The Mangle implementations are extremely light-weight - often just one or two cpp/h pairs per module. You can plug them directly into your @@ -105,15 +106,15 @@ Past and future --------------- Mangle started out as (and still is) a spin-off from OpenMW, another -project I am personally working on ( http://openmw.sourceforge.net ). -OpenMW is an attempt to recreate the engine behind the commercial game +project I am personally working on ( http://openmw.com/ ). OpenMW is +an attempt to recreate the engine behind the commercial game Morrowind, using only open source software. The projects are still tightly interlinked, and they will continue to be until OpenMW is finished. Most near-future work on Mangle will be -focused chiefly on OpenMW at the moment. However I will gladly -implement external contributions and suggestions that are not -OpenMW-related. +focused chiefly on OpenMW at the moment. However I will gladly include +external contributions and suggestions that are not OpenMW-related if +someone sends them to me. Conclusion From 96e456ee09cce69c18ef080a3c14038e22b0722c Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Jun 2010 11:54:04 +0200 Subject: [PATCH 053/111] Added Mangle::Input with SDL and OIS implementation. WIP. --- Doxyfile | 2 +- input/driver.hpp | 95 ++++++++++++++++ input/servers/ois_driver.cpp | 148 +++++++++++++++++++++++++ input/servers/ois_driver.hpp | 48 ++++++++ input/servers/sdl_driver.cpp | 54 +++++++++ input/servers/sdl_driver.hpp | 27 +++++ input/tests/.gitignore | 2 + input/tests/Makefile | 12 ++ input/tests/common.cpp | 34 ++++++ input/tests/ois_driver_test.cpp | 51 +++++++++ input/tests/output/ois_driver_test.out | 5 + input/tests/output/sdl_driver_test.out | 5 + input/tests/plugins.cfg | 12 ++ input/tests/sdl_driver_test.cpp | 16 +++ input/tests/test.sh | 18 +++ testall.sh | 1 + 16 files changed, 529 insertions(+), 1 deletion(-) create mode 100644 input/driver.hpp create mode 100644 input/servers/ois_driver.cpp create mode 100644 input/servers/ois_driver.hpp create mode 100644 input/servers/sdl_driver.cpp create mode 100644 input/servers/sdl_driver.hpp create mode 100644 input/tests/.gitignore create mode 100644 input/tests/Makefile create mode 100644 input/tests/common.cpp create mode 100644 input/tests/ois_driver_test.cpp create mode 100644 input/tests/output/ois_driver_test.out create mode 100644 input/tests/output/sdl_driver_test.out create mode 100644 input/tests/plugins.cfg create mode 100644 input/tests/sdl_driver_test.cpp create mode 100755 input/tests/test.sh diff --git a/Doxyfile b/Doxyfile index f2a1c7455..f3e018002 100644 --- a/Doxyfile +++ b/Doxyfile @@ -564,7 +564,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = sound stream vfs +INPUT = sound stream vfs input # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/input/driver.hpp b/input/driver.hpp new file mode 100644 index 000000000..f8bf9ce09 --- /dev/null +++ b/input/driver.hpp @@ -0,0 +1,95 @@ +#ifndef MANGLE_INPUT_DRIVER_H +#define MANGLE_INPUT_DRIVER_H + +#include "../tools/shared_ptr.hpp" + +namespace Mangle +{ + namespace Input + { + /** Generic callback for input events. The meaning of the + parameters depend on the system producing the events. + */ + struct Event + { + /// Event types + enum EventType + { + EV_Unknown = -1, // Unknown event type + EV_KeyDown = 1, // Key, mouse or other button was pressed + EV_KeyUp = 2, // Key, mouse or other button was released + EV_MouseMove = 3, // Mouse movement (all axis movement?) + EV_Other = 4 // Other event + }; + + /** + Called upon all events. The first parameter give the event + type, the second gives additional data (usually the local + keysym as defined by the driver), and the pointer points to + the full custom event structure provided by the driver (the + type may vary depending on the EventType, this is defined in + the Driver documentation.) + */ + virtual void event(EventType type, int index, const void *p) = 0; + virtual ~Event() {} + }; + + /** Input::Driver is the main interface to any input system that + handles keyboard and/or mouse input, along with any other + input source like joysticks. + + It is really a generalized event system, and could also be + used for non-input related events. The definition of the event + codes and structures are entirely dependent on the + implementation. + + A system-independent key code list will be found in keys.hpp, + and input drivers should privide optional translations to/from + this list for full compatibility. + */ + struct Driver + { + virtual ~Driver() {} + + /** Captures input and produces the relevant events from it. An + event callback must be set with setEvent(), or all events + will be ignored. + */ + virtual void capture() = 0; + + /** Check the state of a given key or button. The key/button + definitions depends on the driver. + */ + virtual bool isDown(int index) = 0; + + /** Show or hide system mouse cursor + */ + virtual void showMouse(bool show) = 0; + + /** Set the event handler for input events. The evt->event() + function is called for each event. The meaning of the index + and *p parameters will be specific to each driver and to + each input system. + */ + void setEvent(Event *evt) + { event = evt; } + + /** Instigate an event. Is used internally for all events, but + can also be called from the outside to "fake" events from + this driver. + */ + void makeEvent(Event::EventType type, int index, const void *p=NULL) + { + if(event) + event->event(type,index,p); + } + + private: + /// Holds the event callback set byt setEvent() + Event *event; + }; + + typedef boost::shared_ptr DriverPtr; + } +} +#endif diff --git a/input/servers/ois_driver.cpp b/input/servers/ois_driver.cpp new file mode 100644 index 000000000..eabe9739d --- /dev/null +++ b/input/servers/ois_driver.cpp @@ -0,0 +1,148 @@ +#include "ois_driver.hpp" + +#include +#include +#include +#include + +#ifdef __APPLE_CC__ +#include +#endif + +using namespace Mangle::Input; +using namespace OIS; + +struct Mangle::Input::OISListener : OIS::KeyListener, OIS::MouseListener +{ + OISDriver &drv; + + OISListener(OISDriver &driver) + : drv(driver) {} + + bool keyPressed( const OIS::KeyEvent &arg ) + { + drv.makeEvent(Event::EV_KeyDown, arg.key, &arg); + return true; + } + + bool keyReleased( const OIS::KeyEvent &arg ) + { + drv.makeEvent(Event::EV_KeyUp, arg.key, &arg); + return true; + } + + bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + // Mouse button events are handled as key events + // TODO: Translate mouse buttons into pseudo-keysyms + drv.makeEvent(Event::EV_KeyDown, -1, &arg); + return true; + } + + bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + // TODO: ditto + drv.makeEvent(Event::EV_KeyUp, -1, &arg); + return true; + } + + bool mouseMoved( const OIS::MouseEvent &arg ) + { + drv.makeEvent(Event::EV_MouseMove, -1, &arg); + return true; + } +}; + +OISDriver::OISDriver(Ogre::RenderWindow *window, bool exclusive) +{ + assert(window); + + size_t windowHnd; + + window->getCustomAttribute("WINDOW", &windowHnd); + + std::ostringstream windowHndStr; + ParamList pl; + + windowHndStr << windowHnd; + pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); + + // Set non-exclusive mouse and keyboard input if the user requested + // it. + if(!exclusive) + { +#if defined OIS_WIN32_PLATFORM + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_FOREGROUND" ))); + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_NONEXCLUSIVE"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_FOREGROUND"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_NONEXCLUSIVE"))); +#elif defined OIS_LINUX_PLATFORM + pl.insert(std::make_pair(std::string("x11_mouse_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_mouse_hide"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_keyboard_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("XAutoRepeatOn"), + std::string("true"))); +#endif + } + +#ifdef __APPLE_CC__ + // Give the application window focus to receive input events + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); +#endif + + inputMgr = InputManager::createInputSystem( pl ); + + // Create all devices + keyboard = static_cast(inputMgr->createInputObject + ( OISKeyboard, true )); + mouse = static_cast(inputMgr->createInputObject + ( OISMouse, true )); + + // Set mouse region + const MouseState &ms = mouse->getMouseState(); + ms.width = window->getWidth(); + ms.height = window->getHeight(); + + // Set up the input listener + listener = new OISListener(*this); + keyboard-> setEventCallback(listener); + mouse-> setEventCallback(listener); +} + +OISDriver::~OISDriver() +{ + // Delete the listener object + if(listener) + delete listener; + + if(inputMgr == NULL) return; + + // Kill the input systems. This will reset input options such as key + // repeat rate. + inputMgr->destroyInputObject(keyboard); + inputMgr->destroyInputObject(mouse); + InputManager::destroyInputSystem(inputMgr); + inputMgr = NULL; +} + +void OISDriver::capture() +{ + // Capture keyboard and mouse events + keyboard->capture(); + mouse->capture(); +} + +bool OISDriver::isDown(int index) +{ + // TODO: Extend to mouse buttons as well + return keyboard->isKeyDown((OIS::KeyCode)index); +} diff --git a/input/servers/ois_driver.hpp b/input/servers/ois_driver.hpp new file mode 100644 index 000000000..ba780c39e --- /dev/null +++ b/input/servers/ois_driver.hpp @@ -0,0 +1,48 @@ +#ifndef MANGLE_INPUT_OIS_DRIVER_H +#define MANGLE_INPUT_OIS_DRIVER_H + +#include "../driver.hpp" + +namespace OIS +{ + class InputManager; + class Mouse; + class Keyboard; +} + +namespace Ogre +{ + class RenderWindow; +} + +namespace Mangle +{ + namespace Input + { + struct OISListener; + + /** Input driver for OIS, the input manager typically used with + Ogre. + */ + struct OISDriver : Driver + { + /// If exclusive=true, then we capture mouse and keyboard from + /// the OS. + OISDriver(Ogre::RenderWindow *window, bool exclusive=true); + ~OISDriver(); + + void capture(); + bool isDown(int index); + /// Not currently supported. + void showMouse(bool) {} + + private: + OIS::InputManager *inputMgr; + OIS::Mouse *mouse; + OIS::Keyboard *keyboard; + + OISListener *listener; + }; + } +} +#endif diff --git a/input/servers/sdl_driver.cpp b/input/servers/sdl_driver.cpp new file mode 100644 index 000000000..f1d92dd41 --- /dev/null +++ b/input/servers/sdl_driver.cpp @@ -0,0 +1,54 @@ +#include "sdl_driver.hpp" + +#include + +using namespace Mangle::Input; + +void SDLDriver::capture() +{ + // Poll for events + SDL_Event evt; + while(SDL_PollEvent(&evt)) + { + Event::EventType type = Event::EV_Unknown; + int index = -1; + + switch(evt.type) + { + // For key events, send the keysym as the index. + case SDL_KEYDOWN: + type = Event::EV_KeyDown; + index = evt.key.keysym.sym; + break; + case SDL_KEYUP: + type = Event::EV_KeyUp; + index = evt.key.keysym.sym; + break; + case SDL_MOUSEMOTION: + type = Event::EV_MouseMove; + break; + // Add more event types later + } + + // Pass the event along, using -1 as index for unidentified + // event types. + makeEvent(type, index, &evt); + } +} + +bool SDLDriver::isDown(int index) +{ + int num; + Uint8 *keys = SDL_GetKeyState(&num); + assert(index >= 0 && index < num); + + // The returned array from GetKeyState is indexed by the + // SDLK_KEYNAME enums and is just a list of bools. If the indexed + // value is true, the button is down. + return keys[index]; +} + +void SDLDriver::showMouse(bool show) +{ + SDL_ShowCursor(show?SDL_ENABLE:SDL_DISABLE); +} diff --git a/input/servers/sdl_driver.hpp b/input/servers/sdl_driver.hpp new file mode 100644 index 000000000..b71346cba --- /dev/null +++ b/input/servers/sdl_driver.hpp @@ -0,0 +1,27 @@ +#ifndef MANGLE_INPUT_SDL_DRIVER_H +#define MANGLE_INPUT_SDL_DRIVER_H + +#include "../driver.hpp" + +namespace Mangle +{ + namespace Input + { + /** Input driver for SDL. As the input system of SDL is seldomly + used alone (most often along with the video system), it is + assumed that you do your own initialization and cleanup of SDL + before and after using this driver. + + The Event.event() calls will be given the proper EV_ type, the + key index (for key up/down events), and a pointer to the full + SDL_Event structure. + */ + struct SDLDriver : Driver + { + void capture(); + bool isDown(int index); + void showMouse(bool); + }; + } +} +#endif diff --git a/input/tests/.gitignore b/input/tests/.gitignore new file mode 100644 index 000000000..460c76f00 --- /dev/null +++ b/input/tests/.gitignore @@ -0,0 +1,2 @@ +*_test +ogre.cfg diff --git a/input/tests/Makefile b/input/tests/Makefile new file mode 100644 index 000000000..b7d7b5b4e --- /dev/null +++ b/input/tests/Makefile @@ -0,0 +1,12 @@ +GCC=g++ + +all: sdl_driver_test ois_driver_test + +sdl_driver_test: sdl_driver_test.cpp + $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL + +ois_driver_test: ois_driver_test.cpp + $(GCC) $< ../servers/ois_driver.cpp -o $@ -I/usr/local/include/OGRE/ -lOgreMain -lOIS -lboost_filesystem + +clean: + rm *_test diff --git a/input/tests/common.cpp b/input/tests/common.cpp new file mode 100644 index 000000000..33f2188e9 --- /dev/null +++ b/input/tests/common.cpp @@ -0,0 +1,34 @@ +#include +#include "../driver.hpp" +#include +using namespace std; +using namespace Mangle::Input; + +Driver *input; + +struct MyCB : Event +{ + void event(Event::EventType type, int i, const void *p) + { + cout << "got event: type=" << type << " index=" << i << endl; + } +} mycb; +void mainLoop(int argc, int quitKey) +{ + cout << "Hold the Q key to quit:\n"; + input->setEvent(&mycb); + while(!input->isDown(quitKey)) + { + input->capture(); + usleep(20000); + + if(argc == 1) + { + cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; + break; + } + } + + delete input; + cout << "\nBye bye!\n"; +} diff --git a/input/tests/ois_driver_test.cpp b/input/tests/ois_driver_test.cpp new file mode 100644 index 000000000..386f24055 --- /dev/null +++ b/input/tests/ois_driver_test.cpp @@ -0,0 +1,51 @@ +#include "common.cpp" + +#include "../servers/ois_driver.hpp" +#include +#include +#include + +bool isFile(const char *name) +{ + boost::filesystem::path cfg_file_path(name); + return boost::filesystem::exists(cfg_file_path); +} + +using namespace Ogre; +using namespace OIS; + +Root *root; +RenderWindow *window; + +void setupOgre() +{ + // Disable logging + new LogManager; + Log *log = LogManager::getSingleton().createLog(""); + log->setDebugOutputEnabled(false); + + bool useConfig = isFile("ogre.cfg"); + + // Set up Root + root = new Root("plugins.cfg", "ogre.cfg", ""); + + // Configure + if(!useConfig) + root->showConfigDialog(); + else + root->restoreConfig(); + + // Initialize OGRE window + window = root->initialise(true, "test", ""); +} + +int main(int argc, char** argv) +{ + setupOgre(); + input = new OISDriver(window); + + mainLoop(argc, KC_Q); + + delete root; + return 0; +} diff --git a/input/tests/output/ois_driver_test.out b/input/tests/output/ois_driver_test.out new file mode 100644 index 000000000..eb9c29bf4 --- /dev/null +++ b/input/tests/output/ois_driver_test.out @@ -0,0 +1,5 @@ +Hold the Q key to quit: +got event: type=3 index=-1 +You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly + +Bye bye! diff --git a/input/tests/output/sdl_driver_test.out b/input/tests/output/sdl_driver_test.out new file mode 100644 index 000000000..54e941fb6 --- /dev/null +++ b/input/tests/output/sdl_driver_test.out @@ -0,0 +1,5 @@ +Hold the Q key to quit: +got event: type=-1 index=-1 +You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly + +Bye bye! diff --git a/input/tests/plugins.cfg b/input/tests/plugins.cfg new file mode 100644 index 000000000..57ec54e1a --- /dev/null +++ b/input/tests/plugins.cfg @@ -0,0 +1,12 @@ +# Defines plugins to load + +# Define plugin folder +PluginFolder=/usr/local/lib/OGRE/ + +# Define plugins +Plugin=RenderSystem_GL +Plugin=Plugin_ParticleFX +Plugin=Plugin_OctreeSceneManager +# Plugin=Plugin_CgProgramManager + + diff --git a/input/tests/sdl_driver_test.cpp b/input/tests/sdl_driver_test.cpp new file mode 100644 index 000000000..5db6dbba8 --- /dev/null +++ b/input/tests/sdl_driver_test.cpp @@ -0,0 +1,16 @@ +#include "common.cpp" + +#include "../servers/sdl_driver.hpp" +#include + +int main(int argc, char** argv) +{ + SDL_Init(SDL_INIT_VIDEO); + SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); + input = new SDLDriver(); + + mainLoop(argc, SDLK_q); + + SDL_Quit(); + return 0; +} diff --git a/input/tests/test.sh b/input/tests/test.sh new file mode 100755 index 000000000..2d07708ad --- /dev/null +++ b/input/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + ./$a | diff output/$a.out - + else + echo "Creating $a.out" + ./$a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/testall.sh b/testall.sh index f38939a19..31b8678c2 100755 --- a/testall.sh +++ b/testall.sh @@ -11,4 +11,5 @@ function run() run stream run vfs run sound +run input run . From 2b5d574ac78102b39c9f4b3c949ea2d468f4c11d Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 1 Jul 2010 10:31:51 +0200 Subject: [PATCH 054/111] Split Event out to separate file. --- input/driver.hpp | 30 ++---------------------------- input/event.hpp | 36 ++++++++++++++++++++++++++++++++++++ input/servers/sdl_driver.cpp | 2 +- input/tests/common.cpp | 2 +- 4 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 input/event.hpp diff --git a/input/driver.hpp b/input/driver.hpp index f8bf9ce09..d95fa206e 100644 --- a/input/driver.hpp +++ b/input/driver.hpp @@ -2,38 +2,12 @@ #define MANGLE_INPUT_DRIVER_H #include "../tools/shared_ptr.hpp" +#include "event.hpp" namespace Mangle { namespace Input { - /** Generic callback for input events. The meaning of the - parameters depend on the system producing the events. - */ - struct Event - { - /// Event types - enum EventType - { - EV_Unknown = -1, // Unknown event type - EV_KeyDown = 1, // Key, mouse or other button was pressed - EV_KeyUp = 2, // Key, mouse or other button was released - EV_MouseMove = 3, // Mouse movement (all axis movement?) - EV_Other = 4 // Other event - }; - - /** - Called upon all events. The first parameter give the event - type, the second gives additional data (usually the local - keysym as defined by the driver), and the pointer points to - the full custom event structure provided by the driver (the - type may vary depending on the EventType, this is defined in - the Driver documentation.) - */ - virtual void event(EventType type, int index, const void *p) = 0; - virtual ~Event() {} - }; - /** Input::Driver is the main interface to any input system that handles keyboard and/or mouse input, along with any other input source like joysticks. @@ -78,7 +52,7 @@ namespace Mangle can also be called from the outside to "fake" events from this driver. */ - void makeEvent(Event::EventType type, int index, const void *p=NULL) + void makeEvent(Event::Type type, int index, const void *p=NULL) { if(event) event->event(type,index,p); diff --git a/input/event.hpp b/input/event.hpp new file mode 100644 index 000000000..5948653bd --- /dev/null +++ b/input/event.hpp @@ -0,0 +1,36 @@ +#ifndef MANGLE_INPUT_EVENT_H +#define MANGLE_INPUT_EVENT_H + +namespace Mangle +{ + namespace Input + { + /** Generic callback for input events. The meaning of the + parameters depend on the system producing the events. + */ + struct Event + { + /// Event types + enum Type + { + EV_Unknown = -1, // Unknown event type + EV_KeyDown = 1, // Key, mouse or other button was pressed + EV_KeyUp = 2, // Key, mouse or other button was released + EV_MouseMove = 3, // Mouse movement (all axis movement?) + EV_Other = 4 // Other event + }; + + /** + Called upon all events. The first parameter give the event + type, the second gives additional data (usually the local + keysym as defined by the driver), and the pointer points to + the full custom event structure provided by the driver (the + type may vary depending on the EventType, this is defined in + the Driver documentation.) + */ + virtual void event(Type type, int index, const void *p) = 0; + virtual ~Event() {} + }; + } +} +#endif diff --git a/input/servers/sdl_driver.cpp b/input/servers/sdl_driver.cpp index f1d92dd41..93884a6e6 100644 --- a/input/servers/sdl_driver.cpp +++ b/input/servers/sdl_driver.cpp @@ -10,7 +10,7 @@ void SDLDriver::capture() SDL_Event evt; while(SDL_PollEvent(&evt)) { - Event::EventType type = Event::EV_Unknown; + Event::Type type = Event::EV_Unknown; int index = -1; switch(evt.type) diff --git a/input/tests/common.cpp b/input/tests/common.cpp index 33f2188e9..47a0c53de 100644 --- a/input/tests/common.cpp +++ b/input/tests/common.cpp @@ -8,7 +8,7 @@ Driver *input; struct MyCB : Event { - void event(Event::EventType type, int i, const void *p) + void event(Event::Type type, int i, const void *p) { cout << "got event: type=" << type << " index=" << i << endl; } From 2139bb78afd59627fe22120f54b2e079caaff387 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 3 Jul 2010 17:11:28 +0200 Subject: [PATCH 055/111] fixed missing NULL initialization in input/driver.hpp --- input/driver.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/input/driver.hpp b/input/driver.hpp index d95fa206e..1c1e95047 100644 --- a/input/driver.hpp +++ b/input/driver.hpp @@ -23,6 +23,7 @@ namespace Mangle */ struct Driver { + Driver() : event(NULL) {} virtual ~Driver() {} /** Captures input and produces the relevant events from it. An From 7583fc3f1bfa6d0fde56c925da959a5e9e50031a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 3 Jul 2010 18:46:21 +0200 Subject: [PATCH 056/111] Added 2D rendring component with SDL backend. --- rend2d/driver.hpp | 61 ++++++++++ rend2d/servers/sdl_driver.cpp | 199 +++++++++++++++++++++++++++++++ rend2d/servers/sdl_driver.hpp | 116 ++++++++++++++++++ rend2d/sprite.hpp | 34 ++++++ rend2d/tests/.gitignore | 1 + rend2d/tests/Makefile | 9 ++ rend2d/tests/output/sdl_test.out | 11 ++ rend2d/tests/sdl_test.cpp | 65 ++++++++++ rend2d/tests/test.sh | 18 +++ rend2d/tests/tile1-blue.png | Bin 0 -> 273 bytes rend2d/tests/tile1-yellow.png | Bin 0 -> 257 bytes testall.sh | 1 + vfs/clients/ogre_archive.hpp | 3 + 13 files changed, 518 insertions(+) create mode 100644 rend2d/driver.hpp create mode 100644 rend2d/servers/sdl_driver.cpp create mode 100644 rend2d/servers/sdl_driver.hpp create mode 100644 rend2d/sprite.hpp create mode 100644 rend2d/tests/.gitignore create mode 100644 rend2d/tests/Makefile create mode 100644 rend2d/tests/output/sdl_test.out create mode 100644 rend2d/tests/sdl_test.cpp create mode 100755 rend2d/tests/test.sh create mode 100644 rend2d/tests/tile1-blue.png create mode 100644 rend2d/tests/tile1-yellow.png diff --git a/rend2d/driver.hpp b/rend2d/driver.hpp new file mode 100644 index 000000000..7366e0462 --- /dev/null +++ b/rend2d/driver.hpp @@ -0,0 +1,61 @@ +#ifndef MANGLE_REND2D_DRIVER_H +#define MANGLE_REND2D_DRIVER_H + +#include +#include "sprite.hpp" + +namespace Mangle +{ + namespace Rend2D + { + /** + The driver is the connection to the backend system that powers + 2D sprite rendering. For example the backend could be SDL or + any other 2D-capable graphics library. + */ + struct Driver + { + /// Get the screen sprite + virtual Sprite *getScreen() = 0; + + /// Sets the video mode. + virtual void setVideoMode(int width, int height, int bpp=32, bool fullscreen=false) = 0; + + /** Update the screen. Until this function is called, none of + the changes written to the screen sprite will be visible. + */ + virtual void update() = 0; + + /// Set the window title, as well as the title of the window + /// when "iconified" + virtual void setWindowTitle(const std::string &title, + const std::string &icon) = 0; + + /// Set the window title + void setWindowTitle(const std::string &title) { setWindowTitle(title,title); } + + /// Load sprite from an image file + virtual Sprite* loadImage(const std::string &file) = 0; + + /// Load a sprite from an image file stored in memory. + virtual Sprite* loadImage(const void* data, size_t size) = 0; + + /** @brief Set gamma value for all colors. + + Note: Setting this in windowed mode will affect the ENTIRE + SCREEN! + */ + virtual void setGamma(float gamma) = 0; + + /// Set gamma individually for red, green, blue + virtual void setGamma(float red, float green, float blue) = 0; + + /// Get screen width + virtual int width() = 0; + + /// Get screen height + virtual int height() = 0; + }; + } +} +#endif diff --git a/rend2d/servers/sdl_driver.cpp b/rend2d/servers/sdl_driver.cpp new file mode 100644 index 000000000..cc102e661 --- /dev/null +++ b/rend2d/servers/sdl_driver.cpp @@ -0,0 +1,199 @@ +#include "sdl_driver.hpp" + +#include +#include +#include "../../tools/str_exception.hpp" +#include + +using namespace Mangle::Rend2D; + +void SDL_Sprite::draw(Sprite *s, // Must be SDL_Sprite + int x, int y, // Destination position + int sx, int sy, // Source position + int w, int h // Amount to draw. -1 means remainder. + ) +{ + // Get source surface + SDL_Sprite *other = dynamic_cast(s); + assert(other != NULL); + SDL_Surface *img = other->getSurface(); + + // Check coordinate validity + assert(sx <= img->w && sy <= img->h); + assert(x <= surface->w && y <= surface->h); + assert(sx >= 0 && sy >= 0); + + // Compute width and height if necessary + if(w == -1) w = img->w - sx; + if(h == -1) h = img->h - sy; + + // Check them if they're valid + assert(w >= 0 && w <= img->w); + assert(h >= 0 && h <= img->h); + + SDL_Rect dest; + dest.x = x; + dest.y = y; + dest.w = w; + dest.h = h; + + SDL_Rect src; + src.x = sx; + src.y = sy; + src.w = w; + src.h = h; + + // Do the Blitman + SDL_BlitSurface(img, &src, surface, &dest); +} + +SDL_Sprite::SDL_Sprite(SDL_Surface *s, bool autoDelete) + : surface(s), autoDel(autoDelete) +{ + assert(surface != NULL); +} + +SDL_Sprite::~SDL_Sprite() +{ + if(autoDel) + SDL_FreeSurface(surface); +} + +void SDL_Sprite::fill(int value) +{ + SDL_FillRect(surface, NULL, value); +} + +int SDL_Sprite::width() { return surface->w; } +int SDL_Sprite::height() { return surface->h; } + +SDLDriver::SDLDriver() : display(NULL), realDisp(NULL), softDouble(false) +{ + if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) + throw str_exception("Error initializing SDL video"); +} +SDLDriver::~SDLDriver() +{ + if(display) delete display; + SDL_Quit(); +} + +void SDLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) +{ + unsigned int flags; + + if(display) delete display; + + if (fullscreen) + // Assume fullscreen mode allows a double-bufferd hardware + // mode. We need more test code for this to be safe though. + flags = SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF; + else + flags = SDL_SWSURFACE; + + // Create the surface and check it + realDisp = SDL_SetVideoMode(width, height, bpp, flags); + if(realDisp == NULL) + throw str_exception("Failed setting SDL video mode"); + + // Code for software double buffering. I haven't found this to be + // any speed advantage at all in windowed mode (it's slower, as one + // would expect.) Not properly tested in fullscreen mode with + // hardware buffers, but it will probably only be an improvement if + // we do excessive writing (ie. write each pixel on average more + // than once) or try to read from the display buffer. + if(softDouble) + { + // Make a new surface with the same attributes as the real + // display surface. + SDL_Surface *back = SDL_DisplayFormat(realDisp); + assert(back != NULL); + + // Create a sprite representing the double buffer + display = new SDL_Sprite(back); + } + else + { + // Create a sprite directly representing the display surface. + // The 'false' parameter means do not autodelete the screen + // surface upon exit (since SDL manages it) + display = new SDL_Sprite(realDisp, false); + } +} + +/// Update the screen +void SDLDriver::update() +{ + // Blit the soft double buffer onto the real display buffer + if(softDouble) + SDL_BlitSurface(display->getSurface(), NULL, realDisp, NULL ); + + if(realDisp) + SDL_Flip(realDisp); +} + +/// Set the window title, as well as the title of the window when +/// "iconified" +void SDLDriver::setWindowTitle(const std::string &title, + const std::string &icon) +{ + SDL_WM_SetCaption( title.c_str(), icon.c_str() ); +} + +// Convert the given surface to display format. +static SDL_Surface* convertImage(SDL_Surface* surf) +{ + if(surf != NULL) + { + // Convert the image to the display buffer format, for faster + // blitting + SDL_Surface *surf2 = SDL_DisplayFormat(surf); + SDL_FreeSurface(surf); + surf = surf2; + } + return surf; +} + +/// Load sprite from an image file, using SDL_image. +Sprite* SDLDriver::loadImage(const std::string &file) +{ + SDL_Surface *surf = IMG_Load(file.c_str()); + surf = convertImage(surf); + if(surf == NULL) + throw str_exception("SDL failed to load image file '" + file + "'"); + return spriteFromSDL(surf); +} + +/// Load sprite from an SDL_RWops structure. autoFree determines +/// whether the RWops struct should be closed/freed after use. +Sprite* SDLDriver::loadImage(SDL_RWops *src, bool autoFree) +{ + SDL_Surface *surf = IMG_Load_RW(src, autoFree); + surf = convertImage(surf); + if(surf == NULL) + throw str_exception("SDL failed to load image"); + return spriteFromSDL(surf); +} + +/// Load a sprite from an image file stored in memory. Uses +/// SDL_image. +Sprite* SDLDriver::loadImage(const void* data, size_t size) +{ + SDL_RWops *rw = SDL_RWFromConstMem(data, size); + return loadImage(rw, true); +} + +void SDLDriver::setGamma(float red, float green, float blue) +{ + SDL_SetGamma(red,green,blue); +} + +/// Convert an existing SDL surface into a sprite +Sprite* SDLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) +{ + assert(surf); + return new SDL_Sprite(surf, autoFree); +} + +void SDLDriver::sleep(int ms) { SDL_Delay(ms); } +unsigned int SDLDriver::ticks() { return SDL_GetTicks(); } diff --git a/rend2d/servers/sdl_driver.hpp b/rend2d/servers/sdl_driver.hpp new file mode 100644 index 000000000..9aeed2f92 --- /dev/null +++ b/rend2d/servers/sdl_driver.hpp @@ -0,0 +1,116 @@ +#ifndef MANGLE_DRAW2D_SDL_H +#define MANGLE_DRAW2D_SDL_H + +#include "../driver.hpp" + +// Predeclarations keep the streets safe at night +struct SDL_Surface; +struct SDL_RWops; + +namespace Mangle +{ + namespace Rend2D + { + /// SDL-implementation of Sprite + struct SDL_Sprite : Sprite + { + /** Draw a sprite in the given position. Can only draw other SDL + sprites. + */ + void draw(Sprite *s, // Must be SDL_Sprite + int x, int y, // Destination position + int sx=0, int sy=0, // Source position + int w=-1, int h=-1 // Amount to draw. -1 means remainder. + ); + + SDL_Sprite(SDL_Surface *s, bool autoDelete=true); + ~SDL_Sprite(); + + // Information retrieval + int width(); + int height(); + SDL_Surface *getSurface() { return surface; } + + // Fill with a given pixel value + void fill(int value); + + private: + // The SDL surface + SDL_Surface* surface; + + // If true, delete this surface when the canvas is destructed + bool autoDel; + }; + + class SDLDriver : public Driver + { + // The main display surface + SDL_Sprite *display; + + // The actual display surface. May or may not be the same + // surface pointed to by 'display' above, depending on the + // softDouble flag. + SDL_Surface *realDisp; + + // If true, we do software double buffering. + bool softDouble; + + public: + SDLDriver(); + ~SDLDriver(); + + /// Sets the video mode. Will create the window if it is not + /// already set up. Note that for SDL, bpp=0 means use current + /// bpp. + void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); + + /// Update the screen + void update(); + + /// Set the window title, as well as the title of the window + /// when "iconified" + void setWindowTitle(const std::string &title, + const std::string &icon); + + // Include overloads from our Glorious parent + using Driver::setWindowTitle; + + /// Load sprite from an image file, using SDL_image. + Sprite* loadImage(const std::string &file); + + /// Load sprite from an SDL_RWops structure. autoFree determines + /// whether the RWops struct should be closed/freed after use. + Sprite* loadImage(SDL_RWops *src, bool autoFree=false); + + /// Load a sprite from an image file stored in memory. Uses + /// SDL_image. + Sprite* loadImage(const void* data, size_t size); + + /// Set gamma value + void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } + + /// Set gamma individually for red, green, blue + void setGamma(float red, float green, float blue); + + /// Convert an existing SDL surface into a sprite + Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); + + // Get width and height + int width() { return display ? display->width() : 0; } + int height() { return display ? display->height() : 0; } + + /// Get the screen sprite + Sprite *getScreen() { return display; } + + /// Not really a graphic-related function, but very + /// handly. Sleeps the given number of milliseconds using + /// SDL_Delay(). + void sleep(int ms); + + /// Get the number of ticks since SDL initialization, using + /// SDL_GetTicks(). + unsigned int ticks(); + }; + } +} +#endif diff --git a/rend2d/sprite.hpp b/rend2d/sprite.hpp new file mode 100644 index 000000000..150d97e4e --- /dev/null +++ b/rend2d/sprite.hpp @@ -0,0 +1,34 @@ +#ifndef MANGLE_REND2D_SPRITE_H +#define MANGLE_REND2D_SPRITE_H + +namespace Mangle +{ + namespace Rend2D + { + /** + A Sprite is either a bitmap to be drawn or an output of area + for blitting other bitmaps, or both. They are created by the + Driver. + */ + struct Sprite + { + /// Draw a sprite in the given position + virtual void draw(Sprite *s, // The sprite to draw + int x, int y, // Destination position + int sx=0, int sy=0, // Source position + int w=-1, int h=-1 // Amount to draw. -1 means remainder. + ) = 0; + + virtual ~Sprite() {} + + // Information retrieval + virtual int width() = 0; + virtual int height() = 0; + + /// Fill the sprite with the given pixel value. The pixel format + /// depends on the format of the sprite. + virtual void fill(int value) = 0; + }; + } +} +#endif diff --git a/rend2d/tests/.gitignore b/rend2d/tests/.gitignore new file mode 100644 index 000000000..814490404 --- /dev/null +++ b/rend2d/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/rend2d/tests/Makefile b/rend2d/tests/Makefile new file mode 100644 index 000000000..c2e16c756 --- /dev/null +++ b/rend2d/tests/Makefile @@ -0,0 +1,9 @@ +GCC=g++ + +all: sdl_test + +sdl_test: sdl_test.cpp + $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image + +clean: + rm *_test diff --git a/rend2d/tests/output/sdl_test.out b/rend2d/tests/output/sdl_test.out new file mode 100644 index 000000000..4528e1a98 --- /dev/null +++ b/rend2d/tests/output/sdl_test.out @@ -0,0 +1,11 @@ +Loading SDL driver. +Creating window. +Current mode: 640x480 +Setting fancy title, cause we like fancy titles. +Loading tile1-blue.png from file. +Loading tile1-yellow.png from memory. +Going bananas. +Taking a breather. +WOW DID YOU SEE THAT!? +Mucking about with the gamma settings +Done. diff --git a/rend2d/tests/sdl_test.cpp b/rend2d/tests/sdl_test.cpp new file mode 100644 index 000000000..0355112e6 --- /dev/null +++ b/rend2d/tests/sdl_test.cpp @@ -0,0 +1,65 @@ +#include +#include + +using namespace std; + +#include "../servers/sdl_driver.hpp" + +using namespace Mangle::Rend2D; + +int main() +{ + cout << "Loading SDL driver.\n"; + SDLDriver sdl; + + cout << "Creating window.\n"; + sdl.setVideoMode(640,480); + cout << "Current mode: " << sdl.width() << "x" << sdl.height() << endl; + + cout << "Setting fancy title, cause we like fancy titles.\n"; + sdl.setWindowTitle("Chief executive window"); + + // Display surface + Sprite *screen = sdl.getScreen(); + + const char* imgName = "tile1-blue.png"; + cout << "Loading " << imgName << " from file.\n"; + Sprite *image = sdl.loadImage(imgName); + + const char* imgName2 = "tile1-yellow.png"; + cout << "Loading " << imgName2 << " from memory.\n"; + Sprite *image2; + { + // This is hard-coded for file sizes below 500 bytes, so obviously + // you shouldn't mess with the image files. + ifstream file(imgName2, ios::binary); + char buf[500]; + file.read(buf, 500); + int size = file.gcount(); + image2 = sdl.loadImage(buf, size); + } + + cout << "Going bananas.\n"; + for(int i=1; i<20; i++) + screen->draw(image, 30*i, 20*i); + + cout << "Taking a breather.\n"; + sdl.update(); + for(int i=1; i<20; i++) + screen->draw(image2, 30*(20-i), 20*i); + sdl.sleep(800); + sdl.update(); + cout << "WOW DID YOU SEE THAT!?\n"; + sdl.sleep(800); + + cout << "Mucking about with the gamma settings\n"; + sdl.setGamma(2.0, 0.1, 0.8); + sdl.sleep(100); + sdl.setGamma(0.6, 2.1, 2.1); + sdl.sleep(100); + sdl.setGamma(1.6); + sdl.sleep(100); + + cout << "Done.\n"; + return 0; +} diff --git a/rend2d/tests/test.sh b/rend2d/tests/test.sh new file mode 100755 index 000000000..2d07708ad --- /dev/null +++ b/rend2d/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + ./$a | diff output/$a.out - + else + echo "Creating $a.out" + ./$a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/rend2d/tests/tile1-blue.png b/rend2d/tests/tile1-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..066e6f8eb932e81d0ab391bc16a3b9e0fb063d7d GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg4HgO>q^~cs6azG);64!_l=ltB< z)VvY~=c3falGGH1^30M91$R&1fbd2>aiF3cPZ!4!jq}MON!e)%tQ!)zB&BycNVusb zWdv<3H_ml_#P;5c#V!`t_IO|Ks0DIaVrPLG@TdWn2J z|KvBBkM=tLPH;V9awjzAOkRtr!tdY5?;n;qvu%fo@uXRM82JyY98GH4whCx3gQu&X J%Q~loCIFm~TL1t6 literal 0 HcmV?d00001 diff --git a/rend2d/tests/tile1-yellow.png b/rend2d/tests/tile1-yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..2aaf9015d313d5cd00915ffbb1df2f0262ea2116 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfEjDo>Htxq)+3JBpk|nMYCC>S|xv6<249-QVi6yBi3gww4 z84B*6z5(HleBwYwd7dtgAsXkCb@CIF6<9YUa9LVOJ4;MTQp*TBJ?;FFMT!6KZ8mro zxg%{yo!B3~hU~W2DM<=~6P7+aaBe@#)q{84nt0F0&sDtcsm}jj_2%Ux)y>}yc6DU# wSS@pBCTF(8x9=wp*PqRuS(uroVp_?}Ab(0lNY${m9cVLyr>mdKI;Vst0EqflTmS$7 literal 0 HcmV?d00001 diff --git a/testall.sh b/testall.sh index 31b8678c2..b93fee215 100755 --- a/testall.sh +++ b/testall.sh @@ -12,4 +12,5 @@ run stream run vfs run sound run input +run rend2d run . diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index 0ea941366..a4986d7b9 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -37,7 +37,10 @@ class MangleArchive : public Ogre::Archive time_t getModifiedTime(const Ogre::String& filename) { return vfs->stat(filename)->time; } + // With both these we support both OGRE 1.6 and 1.7 Ogre::DataStreamPtr open(const Ogre::String& filename) const; + Ogre::DataStreamPtr open(const Ogre::String& filename, bool readOnly) const + { return open(filename); } Ogre::StringVectorPtr list(bool recursive = true, bool dirs = false); Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false); From 18dc065715c12fd8793f8793c7bf834619527399 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 10 Jul 2010 12:32:55 +0200 Subject: [PATCH 057/111] Reworked Input::Event, EventList filter --- input/driver.hpp | 7 ++-- input/event.hpp | 25 ++++++++------ input/filters/eventlist.hpp | 45 ++++++++++++++++++++++++++ input/servers/ois_driver.cpp | 4 +-- input/tests/Makefile | 5 ++- input/tests/common.cpp | 5 +-- input/tests/evtlist_test.cpp | 45 ++++++++++++++++++++++++++ input/tests/output/evtlist_test.out | 12 +++++++ input/tests/output/ois_driver_test.out | 2 +- input/tests/output/sdl_driver_test.out | 2 +- rend2d/driver.hpp | 6 ++-- 11 files changed, 136 insertions(+), 22 deletions(-) create mode 100644 input/filters/eventlist.hpp create mode 100644 input/tests/evtlist_test.cpp create mode 100644 input/tests/output/evtlist_test.out diff --git a/input/driver.hpp b/input/driver.hpp index 1c1e95047..f4ba159c5 100644 --- a/input/driver.hpp +++ b/input/driver.hpp @@ -1,7 +1,6 @@ #ifndef MANGLE_INPUT_DRIVER_H #define MANGLE_INPUT_DRIVER_H -#include "../tools/shared_ptr.hpp" #include "event.hpp" namespace Mangle @@ -23,7 +22,7 @@ namespace Mangle */ struct Driver { - Driver() : event(NULL) {} + Driver() {} virtual ~Driver() {} /** Captures input and produces the relevant events from it. An @@ -46,7 +45,7 @@ namespace Mangle and *p parameters will be specific to each driver and to each input system. */ - void setEvent(Event *evt) + void setEvent(EventPtr evt) { event = evt; } /** Instigate an event. Is used internally for all events, but @@ -61,7 +60,7 @@ namespace Mangle private: /// Holds the event callback set byt setEvent() - Event *event; + EventPtr event; }; typedef boost::shared_ptr DriverPtr; diff --git a/input/event.hpp b/input/event.hpp index 5948653bd..f7ee7e58b 100644 --- a/input/event.hpp +++ b/input/event.hpp @@ -1,6 +1,8 @@ #ifndef MANGLE_INPUT_EVENT_H #define MANGLE_INPUT_EVENT_H +#include "../tools/shared_ptr.hpp" + namespace Mangle { namespace Input @@ -13,24 +15,29 @@ namespace Mangle /// Event types enum Type { - EV_Unknown = -1, // Unknown event type - EV_KeyDown = 1, // Key, mouse or other button was pressed - EV_KeyUp = 2, // Key, mouse or other button was released - EV_MouseMove = 3, // Mouse movement (all axis movement?) - EV_Other = 4 // Other event + EV_Unknown = 1, // Unknown event type + EV_KeyDown = 2, // Keyboard button was pressed + EV_KeyUp = 4, // Keyboard button was released + EV_MouseMove = 8, // Mouse movement + EV_MouseDown = 16, // Mouse button pressed + EV_MouseUp = 32, // Mouse button released + + EV_ALL = 63 // All events }; /** Called upon all events. The first parameter give the event type, the second gives additional data (usually the local - keysym as defined by the driver), and the pointer points to - the full custom event structure provided by the driver (the - type may vary depending on the EventType, this is defined in - the Driver documentation.) + keysym or button index as defined by the driver), and the + pointer points to the full custom event structure provided by + the driver (the type may vary depending on the EventType, + this is defined in the Driver documentation.) */ virtual void event(Type type, int index, const void *p) = 0; virtual ~Event() {} }; + + typedef boost::shared_ptr EventPtr; } } #endif diff --git a/input/filters/eventlist.hpp b/input/filters/eventlist.hpp new file mode 100644 index 000000000..d7703fbc4 --- /dev/null +++ b/input/filters/eventlist.hpp @@ -0,0 +1,45 @@ +#ifndef MANGLE_INPUT_EVENTLIST_H +#define MANGLE_INPUT_EVENTLIST_H + +#include "../event.hpp" +#include + +namespace Mangle +{ + namespace Input + { + /** And Event handler that distributes each event to a list of + other handlers. Supports filtering events by their Type + parameter. + */ + struct EventList : Event + { + struct Filter + { + EventPtr evt; + int flags; + }; + std::vector list; + + void add(EventPtr e, int flags = EV_ALL) + { + Filter f; + f.evt = e; + f.flags = flags; + list.push_back(f); + } + + virtual void event(Type type, int index, const void *p) + { + std::vector::iterator it; + + for(it=list.begin(); it!=list.end(); it++) + { + if(type & it->flags) + it->evt->event(type,index,p); + } + } + }; + } +} +#endif diff --git a/input/servers/ois_driver.cpp b/input/servers/ois_driver.cpp index eabe9739d..2071b91ea 100644 --- a/input/servers/ois_driver.cpp +++ b/input/servers/ois_driver.cpp @@ -35,14 +35,14 @@ struct Mangle::Input::OISListener : OIS::KeyListener, OIS::MouseListener { // Mouse button events are handled as key events // TODO: Translate mouse buttons into pseudo-keysyms - drv.makeEvent(Event::EV_KeyDown, -1, &arg); + drv.makeEvent(Event::EV_MouseDown, id, &arg); return true; } bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) { // TODO: ditto - drv.makeEvent(Event::EV_KeyUp, -1, &arg); + drv.makeEvent(Event::EV_MouseUp, id, &arg); return true; } diff --git a/input/tests/Makefile b/input/tests/Makefile index b7d7b5b4e..8b7663dd5 100644 --- a/input/tests/Makefile +++ b/input/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -all: sdl_driver_test ois_driver_test +all: sdl_driver_test ois_driver_test evtlist_test sdl_driver_test: sdl_driver_test.cpp $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL @@ -8,5 +8,8 @@ sdl_driver_test: sdl_driver_test.cpp ois_driver_test: ois_driver_test.cpp $(GCC) $< ../servers/ois_driver.cpp -o $@ -I/usr/local/include/OGRE/ -lOgreMain -lOIS -lboost_filesystem +evtlist_test: evtlist_test.cpp ../filters/eventlist.hpp ../event.hpp + $(GCC) $< -o $@ + clean: rm *_test diff --git a/input/tests/common.cpp b/input/tests/common.cpp index 47a0c53de..0c7c76466 100644 --- a/input/tests/common.cpp +++ b/input/tests/common.cpp @@ -12,11 +12,12 @@ struct MyCB : Event { cout << "got event: type=" << type << " index=" << i << endl; } -} mycb; +}; + void mainLoop(int argc, int quitKey) { cout << "Hold the Q key to quit:\n"; - input->setEvent(&mycb); + input->setEvent(EventPtr(new MyCB)); while(!input->isDown(quitKey)) { input->capture(); diff --git a/input/tests/evtlist_test.cpp b/input/tests/evtlist_test.cpp new file mode 100644 index 000000000..fbd980cbd --- /dev/null +++ b/input/tests/evtlist_test.cpp @@ -0,0 +1,45 @@ +#include +#include "../filters/eventlist.hpp" + +using namespace std; +using namespace Mangle::Input; + +struct MyEvent : Event +{ + int ii; + MyEvent(int i) : ii(i) {} + + void event(Event::Type type, int i, const void *p) + { + cout << " #" << ii << " got event: type=" << type << " index=" << i << endl; + } +}; + +EventList lst; + +int iii=1; +void make(int flags) +{ + lst.add(EventPtr(new MyEvent(iii++)), flags); +} + +void send(Event::Type type) +{ + cout << "Sending type " << type << endl; + lst.event(type,0,NULL); +} + +int main() +{ + make(Event::EV_ALL); + make(Event::EV_KeyDown); + make(Event::EV_KeyUp | Event::EV_MouseDown); + + send(Event::EV_Unknown); + send(Event::EV_KeyDown); + send(Event::EV_KeyUp); + send(Event::EV_MouseDown); + + cout << "Enough of that\n"; + return 0; +} diff --git a/input/tests/output/evtlist_test.out b/input/tests/output/evtlist_test.out new file mode 100644 index 000000000..180dcc58a --- /dev/null +++ b/input/tests/output/evtlist_test.out @@ -0,0 +1,12 @@ +Sending type 1 + #1 got event: type=1 index=0 +Sending type 2 + #1 got event: type=2 index=0 + #2 got event: type=2 index=0 +Sending type 4 + #1 got event: type=4 index=0 + #3 got event: type=4 index=0 +Sending type 16 + #1 got event: type=16 index=0 + #3 got event: type=16 index=0 +Enough of that diff --git a/input/tests/output/ois_driver_test.out b/input/tests/output/ois_driver_test.out index eb9c29bf4..7d273fd46 100644 --- a/input/tests/output/ois_driver_test.out +++ b/input/tests/output/ois_driver_test.out @@ -1,5 +1,5 @@ Hold the Q key to quit: -got event: type=3 index=-1 +got event: type=8 index=-1 You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly Bye bye! diff --git a/input/tests/output/sdl_driver_test.out b/input/tests/output/sdl_driver_test.out index 54e941fb6..2df2e4014 100644 --- a/input/tests/output/sdl_driver_test.out +++ b/input/tests/output/sdl_driver_test.out @@ -1,5 +1,5 @@ Hold the Q key to quit: -got event: type=-1 index=-1 +got event: type=1 index=-1 You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly Bye bye! diff --git a/rend2d/driver.hpp b/rend2d/driver.hpp index 7366e0462..08a15b0ae 100644 --- a/rend2d/driver.hpp +++ b/rend2d/driver.hpp @@ -34,10 +34,12 @@ namespace Mangle /// Set the window title void setWindowTitle(const std::string &title) { setWindowTitle(title,title); } - /// Load sprite from an image file + /// Load sprite from an image file. Thows an exception on + /// failure. virtual Sprite* loadImage(const std::string &file) = 0; - /// Load a sprite from an image file stored in memory. + /// Load a sprite from an image file stored in memory. Throws + /// exception on failure. virtual Sprite* loadImage(const void* data, size_t size) = 0; /** @brief Set gamma value for all colors. From 9f21081c13f70bc41e20b50c19ed2dac9b458833 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 10 Jul 2010 13:29:44 +0200 Subject: [PATCH 058/111] Added ogre input capturer --- input/clients/ogre_input_capture.hpp | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 input/clients/ogre_input_capture.hpp diff --git a/input/clients/ogre_input_capture.hpp b/input/clients/ogre_input_capture.hpp new file mode 100644 index 000000000..6a7beae4d --- /dev/null +++ b/input/clients/ogre_input_capture.hpp @@ -0,0 +1,29 @@ +#ifndef MANGLE_INPUT_OGREINPUTFRAME_H +#define MANGLE_INPUT_OGREINPUTFRAME_H + +/* + This Ogre FrameListener calls capture() on an input driver every frame. + */ + +#include +#include "../driver.hpp" + +namespace Mangle { +namespace Input { + + struct OgreInputCapture : Ogre::FrameListener + { + Mangle::Input::Driver &driver; + + ExitListener(Mangle::Input::Driver &drv) + : driver(drv) {} + + bool frameStarted(const FrameEvent &evt) + { + driver.capture(); + return true; + } + }; +} + +#endif From c7b179d6546688208528c8eef681d42b7c1ec7be Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 16 Jul 2010 21:44:47 +0200 Subject: [PATCH 059/111] Added EV_Keyboard and EV_Mouse catch-all event flags --- input/event.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/input/event.hpp b/input/event.hpp index f7ee7e58b..dc7b47088 100644 --- a/input/event.hpp +++ b/input/event.hpp @@ -18,9 +18,12 @@ namespace Mangle EV_Unknown = 1, // Unknown event type EV_KeyDown = 2, // Keyboard button was pressed EV_KeyUp = 4, // Keyboard button was released + EV_Keyboard = 6, // All keyboard events + EV_MouseMove = 8, // Mouse movement EV_MouseDown = 16, // Mouse button pressed EV_MouseUp = 32, // Mouse button released + EV_Mouse = 56, // All mouse events EV_ALL = 63 // All events }; From 4692375491fb16da59642022c8d7c891d68ba665 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 27 Jul 2010 12:12:06 +0200 Subject: [PATCH 060/111] Added convenience classes for connecting sound to Ogre --- input/clients/ogre_input_capture.hpp | 6 +-- sound/clients/ogre_listener_mover.hpp | 69 +++++++++++++++++++++++++++ sound/clients/ogre_output_updater.hpp | 31 ++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 sound/clients/ogre_listener_mover.hpp create mode 100644 sound/clients/ogre_output_updater.hpp diff --git a/input/clients/ogre_input_capture.hpp b/input/clients/ogre_input_capture.hpp index 6a7beae4d..2e77dc10b 100644 --- a/input/clients/ogre_input_capture.hpp +++ b/input/clients/ogre_input_capture.hpp @@ -15,15 +15,15 @@ namespace Input { { Mangle::Input::Driver &driver; - ExitListener(Mangle::Input::Driver &drv) + OgreInputCapture(Mangle::Input::Driver &drv) : driver(drv) {} - bool frameStarted(const FrameEvent &evt) + bool frameStarted(const Ogre::FrameEvent &evt) { driver.capture(); return true; } }; -} +}} #endif diff --git a/sound/clients/ogre_listener_mover.hpp b/sound/clients/ogre_listener_mover.hpp new file mode 100644 index 000000000..d1cfcb65c --- /dev/null +++ b/sound/clients/ogre_listener_mover.hpp @@ -0,0 +1,69 @@ +#ifndef MANGLE_SOUND_OGRELISTENERMOVER_H +#define MANGLE_SOUND_OGRELISTENERMOVER_H + +#include +#include +#include "../output.hpp" + +namespace Mangle { +namespace Sound { + + /** This class lets a sound listener (ie. the SoundFactory) track a + given camera in Ogre3D. The poisition and orientation of the + listener will be updated to match the camera whenever the camera + is moved. + */ + struct OgreListenerMover : Ogre::Camera::Listener + { + OgreListenerMover(Mangle::Sound::SoundFactoryPtr snd) + : soundFact(snd), camera(NULL) + {} + + /// Follow a camera. WARNING: This will OVERRIDE any other + /// MovableObject::Listener you may have attached to the camera. + void followCamera(Ogre::Camera *cam) + { + camera = cam; + camera->addListener(this); + } + + private: + Mangle::Sound::SoundFactoryPtr soundFact; + Ogre::Camera *camera; + Ogre::Vector3 pos, dir, up; + + /// From Camera::Listener. This is called once per + /// frame. Unfortunately, Ogre doesn't allow us to be notified + /// only when the camera itself has moved, so we must poll every + /// frame. + void cameraPreRenderScene(Ogre::Camera *cam) + { + assert(cam == camera); + + Ogre::Vector3 nPos, nDir, nUp; + + nPos = camera->getPosition(); + nDir = camera->getDirection(); + nUp = camera->getUp(); + + // Don't bother the sound system needlessly + if(nDir != dir || nPos != pos || nUp != up) + { + pos = nPos; + dir = nDir; + up = nUp; + + soundFact->setListenerPos(pos.x, pos.y, pos.z, + dir.x, dir.y, dir.z, + up.x, up.y, up.z); + } + } + + void cameraDestroyed(Ogre::Camera *cam) + { + assert(cam == camera); + camera = NULL; + } + }; +}} +#endif diff --git a/sound/clients/ogre_output_updater.hpp b/sound/clients/ogre_output_updater.hpp new file mode 100644 index 000000000..5072e9f1e --- /dev/null +++ b/sound/clients/ogre_output_updater.hpp @@ -0,0 +1,31 @@ +#ifndef MANGLE_SOUND_OGREUPDATER_H +#define MANGLE_SOUND_OGREUPDATER_H + +/* + This Ogre FrameListener calls update on a SoundFactory + */ + +#include +#include "../output.hpp" +#include + +namespace Mangle { +namespace Sound { + + struct OgreOutputUpdater : Ogre::FrameListener + { + Mangle::Sound::SoundFactory &driver; + + OgreOutputUpdater(Mangle::Sound::SoundFactory &drv) + : driver(drv) + { assert(drv.needsUpdate); } + + bool frameStarted(const Ogre::FrameEvent &evt) + { + driver.update(); + return true; + } + }; +}} + +#endif From 687f20344e86356148118a3db920ae0f6c69c573 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 3 Aug 2010 14:25:56 +0200 Subject: [PATCH 061/111] Added EventListPtr typedef --- input/filters/eventlist.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input/filters/eventlist.hpp b/input/filters/eventlist.hpp index d7703fbc4..b3e2ff8f2 100644 --- a/input/filters/eventlist.hpp +++ b/input/filters/eventlist.hpp @@ -40,6 +40,8 @@ namespace Mangle } } }; + + typedef boost::shared_ptr EventListPtr; } } #endif From 21d399cb813cdcb1e5854a268ed34913d1d4c8fa Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 3 Aug 2010 18:02:44 +0200 Subject: [PATCH 062/111] Modified Stream::PureFilter to allowed delayed setting of stream --- stream/filters/pure_filter.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stream/filters/pure_filter.hpp b/stream/filters/pure_filter.hpp index e4ae24375..fe95e2075 100644 --- a/stream/filters/pure_filter.hpp +++ b/stream/filters/pure_filter.hpp @@ -15,9 +15,13 @@ class PureFilter : public Stream StreamPtr src; public: + PureFilter() {} PureFilter(StreamPtr _src) - : src(_src) + { setStream(_src); } + + void setStream(StreamPtr _src) { + src = _src; isSeekable = src->isSeekable; hasPosition = src->hasPosition; hasSize = src->hasSize; From 71366d9a0773e8214d252e7c8c4a1e3830069fd1 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 4 Aug 2010 12:20:46 +0200 Subject: [PATCH 063/111] Added writing capability to streams. Created OutFileStream. --- stream/clients/ogre_datastream.hpp | 9 ++- stream/filters/pure_filter.hpp | 2 + stream/filters/slice_stream.hpp | 21 ++++++- stream/servers/outfile_stream.hpp | 41 +++++++++++++ stream/servers/std_ostream.hpp | 79 +++++++++++++++++++++++++ stream/servers/std_stream.hpp | 2 +- stream/stream.hpp | 14 ++++- stream/tests/.gitignore | 1 + stream/tests/Makefile | 5 +- stream/tests/file_write_test.cpp | 41 +++++++++++++ stream/tests/output/file_write_test.out | 12 ++++ vfs/clients/ogre_archive.hpp | 6 +- 12 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 stream/servers/outfile_stream.hpp create mode 100644 stream/servers/std_ostream.hpp create mode 100644 stream/tests/file_write_test.cpp create mode 100644 stream/tests/output/file_write_test.out diff --git a/stream/clients/ogre_datastream.hpp b/stream/clients/ogre_datastream.hpp index 7b4106001..76a6f20cf 100644 --- a/stream/clients/ogre_datastream.hpp +++ b/stream/clients/ogre_datastream.hpp @@ -23,6 +23,10 @@ class Mangle2OgreStream : public Ogre::DataStream // Get the size, if possible if(inp->hasSize) mSize = inp->size(); + + // Allow writing if inp supports it + if(inp->isWritable) + mAccess |= Ogre::DataStream::WRITE; } public: @@ -37,7 +41,10 @@ class Mangle2OgreStream : public Ogre::DataStream // Only implement the DataStream functions we have to implement size_t read(void *buf, size_t count) - { return inp->read(buf,count); } + { return inp->read(buf,count); } + + size_t write(const void *buf, size_t count) + { assert(inp->isWritable); return inp->write(buf,count); } void skip(long count) { diff --git a/stream/filters/pure_filter.hpp b/stream/filters/pure_filter.hpp index fe95e2075..ed1690d8c 100644 --- a/stream/filters/pure_filter.hpp +++ b/stream/filters/pure_filter.hpp @@ -23,12 +23,14 @@ class PureFilter : public Stream { src = _src; isSeekable = src->isSeekable; + isWritable = src->isWritable; hasPosition = src->hasPosition; hasSize = src->hasSize; hasPtr = src->hasPtr; } size_t read(void *buf, size_t count) { return src->read(buf, count); } + size_t write(const void *buf, size_t count) { return src->write(buf,count); } void seek(size_t pos) { src->seek(pos); } size_t tell() const { return src->tell(); } size_t size() const { return src->size(); } diff --git a/stream/filters/slice_stream.hpp b/stream/filters/slice_stream.hpp index 49f241cfc..20f202bdc 100644 --- a/stream/filters/slice_stream.hpp +++ b/stream/filters/slice_stream.hpp @@ -27,6 +27,7 @@ class SliceStream : public Stream hasPosition = true; hasSize = true; hasPtr = src->hasPtr; + isWritable = src->isWritable; } size_t read(void *buf, size_t count) @@ -35,7 +36,7 @@ class SliceStream : public Stream if(count > length-pos) count = length-pos; - // Seek into place and reading + // Seek into place and start reading src->seek(offset+pos); count = src->read(buf, count); @@ -44,6 +45,24 @@ class SliceStream : public Stream return count; } + // Note that writing to a slice does not allow you to append data, + // you may only overwrite existing data. + size_t write(const void *buf, size_t count) + { + assert(isWritable); + // Check that we're not reading past our slice + if(count > length-pos) + count = length-pos; + + // Seek into place and action + src->seek(offset+pos); + count = src->write(buf, count); + + pos += count; + assert(pos <= length); + return count; + } + void seek(size_t _pos) { pos = _pos; diff --git a/stream/servers/outfile_stream.hpp b/stream/servers/outfile_stream.hpp new file mode 100644 index 000000000..37504d78c --- /dev/null +++ b/stream/servers/outfile_stream.hpp @@ -0,0 +1,41 @@ +#ifndef MANGLE_STREAM_FILESERVER_H +#define MANGLE_STREAM_FILESERVER_H + +#include "std_ostream.hpp" +#include + +namespace Mangle { +namespace Stream { + +/** File stream based on std::ofstream, only supports writing. + */ +class OutFileStream : public StdOStream +{ + std::ofstream file; + + public: + /** + By default we overwrite the file. If append=true, then we will + open an existing file and seek to the end instead. + */ + OutFileStream(const std::string &name, bool append=false) + : StdOStream(&file) + { + std::ios::openmode mode = std::ios::binary; + if(append) + mode |= std::ios::app; + else + mode |= std::ios::trunc; + + file.open(name.c_str(), mode); + + if(file.fail()) + throw str_exception("OutFileStream: failed to open file " + name); + } + ~OutFileStream() { file.close(); } +}; + +typedef boost::shared_ptr OutFileStreamPtr; + +}} // namespaces +#endif diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp new file mode 100644 index 000000000..32999e8c1 --- /dev/null +++ b/stream/servers/std_ostream.hpp @@ -0,0 +1,79 @@ +#ifndef MANGLE_STREAM_STDIOSERVER_H +#define MANGLE_STREAM_STDIOSERVER_H + +#include "../stream.hpp" +#include +#include "../../tools/str_exception.hpp" + +namespace Mangle { +namespace Stream { + +/** Simple wrapper for std::ostream, only supports output. + */ +class StdOStream : public Stream +{ + std::ostream *inf; + + static void fail(const std::string &msg) + { throw str_exception("StdOStream: " + msg); } + + public: + StdOStream(std::ostream *_inf) + : inf(_inf) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + isWritable = true; + } + + size_t read(void*,size_t) + { + assert(0&&"reading not supported by StdOStream"); + } + + size_t write(const void* buf, size_t len) + { + inf->write((const char*)buf, len); + if(inf->fail()) + fail("error writing to stream"); + + // Unfortunately, stupid std::ostream doesn't have a pcount() to + // match gcount() for input. In general the std::iostream system + // is an idiotically designed stream library. + return len; + } + + void seek(size_t pos) + { + inf->seekp(pos); + if(inf->fail()) + fail("seek error"); + } + + size_t tell() const + // Hack around the fact that ifstream->tellp() isn't const + { return ((StdOStream*)this)->inf->tellp(); } + + size_t size() const + { + // Use the standard iostream size hack, terrible as it is. + std::streampos pos = inf->tellp(); + inf->seekp(0, std::ios::end); + size_t res = inf->tellp(); + inf->seekp(pos); + + if(inf->fail()) + fail("could not get stream size"); + + return res; + } + + bool eof() const + { return inf->eof(); } +}; + +typedef boost::shared_ptr StdOStreamPtr; + +}} // namespaces +#endif diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index bb8bb6cbc..448306e5c 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -8,7 +8,7 @@ namespace Mangle { namespace Stream { -/** Simplest wrapper for std::istream. +/** Simple wrapper for std::istream. */ class StdStream : public Stream { diff --git a/stream/stream.hpp b/stream/stream.hpp index a195fe321..bc369f6cd 100644 --- a/stream/stream.hpp +++ b/stream/stream.hpp @@ -23,13 +23,17 @@ class Stream /// If true, size() works bool hasSize; + /// If true, write() works. Writing through pointer operations is + /// not supported. + bool isWritable; + /// If true, the getPtr() functions work bool hasPtr; /// Initialize all bools to false by default Stream() : isSeekable(false), hasPosition(false), hasSize(false), - hasPtr(false) {} + isWritable(false), hasPtr(false) {} /// Virtual destructor virtual ~Stream() {} @@ -40,6 +44,14 @@ class Stream */ virtual size_t read(void* buf, size_t count) = 0; + /** Write a given number of bytes from the stream. Semantics is + similar to read(). Only valid if isWritable is true + + Since most implementations do NOT support writing we default to + an assert(0) here. + */ + virtual size_t write(const void *buf, size_t count) { assert(0); return 0; } + /// Seek to an absolute position in this stream. Not all streams are /// seekable. virtual void seek(size_t pos) = 0; diff --git a/stream/tests/.gitignore b/stream/tests/.gitignore index 814490404..9dfd618e2 100644 --- a/stream/tests/.gitignore +++ b/stream/tests/.gitignore @@ -1 +1,2 @@ *_test +test.file diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 8504de741..924a252c3 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test +all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -15,6 +15,9 @@ audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_fi file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ +file_write_test: file_write_test.cpp ../stream.hpp ../servers/outfile_stream.hpp ../servers/std_ostream.hpp + $(GCC) $< -o $@ + memory_server_test: memory_server_test.cpp ../stream.hpp ../servers/memory_stream.hpp $(GCC) $< -o $@ diff --git a/stream/tests/file_write_test.cpp b/stream/tests/file_write_test.cpp new file mode 100644 index 000000000..c6c61fcca --- /dev/null +++ b/stream/tests/file_write_test.cpp @@ -0,0 +1,41 @@ +#include "../servers/outfile_stream.hpp" +#include + +using namespace Mangle::Stream; +using namespace std; + +void print(Stream &str) +{ + cout << "size=" << str.size() + << " pos=" << str.tell() + << " eof=" << str.eof() + << endl; +} + +int main() +{ + { + cout << "\nCreating file\n"; + OutFileStream out("test.file"); + print(out); + out.write("hello",5); + print(out); + } + + { + cout << "\nAppending to file\n"; + OutFileStream out("test.file", true); + print(out); + out.write(" again\n",7); + print(out); + } + + { + cout << "\nOverwriting file\n"; + OutFileStream out("test.file"); + print(out); + out.write("overwrite!\n",11); + print(out); + } + return 0; +} diff --git a/stream/tests/output/file_write_test.out b/stream/tests/output/file_write_test.out new file mode 100644 index 000000000..34b07c49f --- /dev/null +++ b/stream/tests/output/file_write_test.out @@ -0,0 +1,12 @@ + +Creating file +size=0 pos=0 eof=0 +size=5 pos=5 eof=0 + +Appending to file +size=5 pos=5 eof=0 +size=12 pos=12 eof=0 + +Overwriting file +size=0 pos=0 eof=0 +size=11 pos=11 eof=0 diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index a4986d7b9..77191881a 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -11,9 +11,9 @@ namespace VFS { /** An OGRE Archive implementation that wraps a Mangle::VFS filesystem. - This has been built and tested against OGRE 1.6.2. You might have - to make your own modifications if you're working with newer (or - older) versions. + This has been built and tested against OGRE 1.6.2, and has been + extended for OGRE 1.7. You might have to make your own + modifications if you're working with newer (or older) versions. */ class MangleArchive : public Ogre::Archive { From d3f0932b20762bc4ac342ade7926dffac2ccbd00 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 4 Aug 2010 12:47:48 +0200 Subject: [PATCH 064/111] Added Stream::flush() --- stream/filters/pure_filter.hpp | 1 + stream/filters/slice_stream.hpp | 1 + stream/servers/std_ostream.hpp | 11 +++++++---- stream/stream.hpp | 3 +++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/stream/filters/pure_filter.hpp b/stream/filters/pure_filter.hpp index ed1690d8c..f0ce91f87 100644 --- a/stream/filters/pure_filter.hpp +++ b/stream/filters/pure_filter.hpp @@ -31,6 +31,7 @@ class PureFilter : public Stream size_t read(void *buf, size_t count) { return src->read(buf, count); } size_t write(const void *buf, size_t count) { return src->write(buf,count); } + void flush() { src->flush(); } void seek(size_t pos) { src->seek(pos); } size_t tell() const { return src->tell(); } size_t size() const { return src->size(); } diff --git a/stream/filters/slice_stream.hpp b/stream/filters/slice_stream.hpp index 20f202bdc..6337b9d57 100644 --- a/stream/filters/slice_stream.hpp +++ b/stream/filters/slice_stream.hpp @@ -72,6 +72,7 @@ class SliceStream : public Stream bool eof() const { return pos == length; } size_t tell() const { return pos; } size_t size() const { return length; } + void flush() { src->flush(); } const void *getPtr() { return getPtr(0, length); } const void *getPtr(size_t size) diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp index 32999e8c1..9ec6609e9 100644 --- a/stream/servers/std_ostream.hpp +++ b/stream/servers/std_ostream.hpp @@ -37,13 +37,16 @@ class StdOStream : public Stream inf->write((const char*)buf, len); if(inf->fail()) fail("error writing to stream"); - - // Unfortunately, stupid std::ostream doesn't have a pcount() to - // match gcount() for input. In general the std::iostream system - // is an idiotically designed stream library. + // Just return len, but that is ok. The only cases where we would + // return less than len is when an error occured. return len; } + void flush() + { + inf->flush(); + } + void seek(size_t pos) { inf->seekp(pos); diff --git a/stream/stream.hpp b/stream/stream.hpp index bc369f6cd..aa9c14a9e 100644 --- a/stream/stream.hpp +++ b/stream/stream.hpp @@ -52,6 +52,9 @@ class Stream */ virtual size_t write(const void *buf, size_t count) { assert(0); return 0; } + /// Flush an output stream. Does nothing for non-writing streams. + virtual void flush() {} + /// Seek to an absolute position in this stream. Not all streams are /// seekable. virtual void seek(size_t pos) = 0; From 073cc246b6007099888b7718f23e5f04085fe30a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 4 Aug 2010 19:59:11 +0200 Subject: [PATCH 065/111] Fixed include guards on std::ostream wrappers --- stream/servers/outfile_stream.hpp | 4 ++-- stream/servers/std_ostream.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stream/servers/outfile_stream.hpp b/stream/servers/outfile_stream.hpp index 37504d78c..2946ff853 100644 --- a/stream/servers/outfile_stream.hpp +++ b/stream/servers/outfile_stream.hpp @@ -1,5 +1,5 @@ -#ifndef MANGLE_STREAM_FILESERVER_H -#define MANGLE_STREAM_FILESERVER_H +#ifndef MANGLE_OSTREAM_FILESERVER_H +#define MANGLE_OSTREAM_FILESERVER_H #include "std_ostream.hpp" #include diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp index 9ec6609e9..f655477ff 100644 --- a/stream/servers/std_ostream.hpp +++ b/stream/servers/std_ostream.hpp @@ -1,5 +1,5 @@ -#ifndef MANGLE_STREAM_STDIOSERVER_H -#define MANGLE_STREAM_STDIOSERVER_H +#ifndef MANGLE_OSTREAM_STDIOSERVER_H +#define MANGLE_OSTREAM_STDIOSERVER_H #include "../stream.hpp" #include From e181b71cc9d1a647734547d1327ad1185eac5a90 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 5 Aug 2010 12:14:42 +0200 Subject: [PATCH 066/111] Minor fix to input_filter.hpp --- sound/filters/input_filter.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/filters/input_filter.hpp b/sound/filters/input_filter.hpp index 267858de1..00ee18766 100644 --- a/sound/filters/input_filter.hpp +++ b/sound/filters/input_filter.hpp @@ -10,13 +10,12 @@ namespace Sound { /** @brief This filter class adds file loading capabilities to a - Sound::SoundFactory class, by associating an SampleSourceLoader - with it. + Sound::SoundFactory class, by associating a SampleSourceLoader with + it. The class takes an existing SoundFactory able to load streams, and - associates an SampleSourceLoader with it. The combined class is - able to load files directly. -*/ + associates a SampleSourceLoader with it. The combined class is able + to load files directly. */ class InputFilter : public SoundFactory { protected: @@ -45,7 +44,7 @@ class InputFilter : public SoundFactory // Both these should be true, or the use of this class is pretty // pointless canLoadSource = snd->canLoadSource; - canLoadFile = canLoadSource; + canLoadFile = inp->canLoadFile; assert(canLoadSource && canLoadFile); } From 3a6912b04c94866a4698c2fedf5f83f9f85cf792 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 5 Aug 2010 12:35:17 +0200 Subject: [PATCH 067/111] Added pure sound filters --- sound/filters/pure_filter.hpp | 67 +++++++++++++++++++++++++++++++++++ sound/output.hpp | 5 +-- 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 sound/filters/pure_filter.hpp diff --git a/sound/filters/pure_filter.hpp b/sound/filters/pure_filter.hpp new file mode 100644 index 000000000..5238d788a --- /dev/null +++ b/sound/filters/pure_filter.hpp @@ -0,0 +1,67 @@ +#ifndef MANGLE_SOUND_OUTPUT_PUREFILTER_H +#define MANGLE_SOUND_OUTPUT_PUREFILTER_H + +#include "../output.hpp" + +namespace Mangle +{ + namespace Sound + { + // For use in writing other filters + class SoundFilter : public Sound + { + SoundPtr client; + + public: + SoundFilter(SoundPtr c) : client(c) {} + void play() { client->play(); } + void stop() { client->stop(); } + void pause() { client->pause(); } + bool isPlaying() const { return client->isPlaying(); } + void setVolume(float f) { client->setVolume(f); } + void setPan(float f) { client->setPan(f); } + void setPos(float x, float y, float z) + { client->setPos(x,y,z); } + void setRepeat(bool b) { client->setRepeat(b); } + void setStreaming(bool b) { client->setStreaming(b); } + + // The clone() function is not implemented here, as you will + // almost certainly want to override it yourself + }; + + class FactoryFilter : public SoundFactory + { + SoundFactoryPtr client; + + public: + FactoryFilter(SoundFactoryPtr c) : client(c) + { + needsUpdate = client->needsUpdate; + has3D = client->has3D; + canLoadFile = client->canLoadFile; + canLoadStream = client->canLoadStream; + canLoadSource = client->canLoadSource; + } + + SoundPtr loadRaw(SampleSourcePtr input) + { return client->loadRaw(input); } + + SoundPtr load(Stream::StreamPtr input) + { return client->load(input); } + + SoundPtr load(const std::string &file) + { return client->load(file); } + + void update() + { client->update(); } + + void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { + client->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); + } + }; + } +} +#endif diff --git a/sound/output.hpp b/sound/output.hpp index 2ead277bc..404dcf4f8 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -2,8 +2,9 @@ #define MANGLE_SOUND_OUTPUT_H #include -#include "source.hpp" +#include +#include "source.hpp" #include "../stream/stream.hpp" namespace Mangle { @@ -149,7 +150,7 @@ class SoundFactory buffers and similar tasks. Implementations that do not need this should set needsUpdate to false. */ - virtual void update() = 0; + virtual void update() { assert(0); } /// Set listener position (coordinates, front and up vectors) /** From 87f6e739759244cef8e9730b8f94e628b8d64681 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 11 Aug 2010 11:59:08 +0200 Subject: [PATCH 068/111] pure sound filter: made 'client' protected --- sound/filters/pure_filter.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/filters/pure_filter.hpp b/sound/filters/pure_filter.hpp index 5238d788a..ffa49d35a 100644 --- a/sound/filters/pure_filter.hpp +++ b/sound/filters/pure_filter.hpp @@ -10,6 +10,7 @@ namespace Mangle // For use in writing other filters class SoundFilter : public Sound { + protected: SoundPtr client; public: @@ -31,6 +32,7 @@ namespace Mangle class FactoryFilter : public SoundFactory { + protected: SoundFactoryPtr client; public: From fb2d077ca9935374dd46576e643d111feb325c90 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 12 Aug 2010 15:52:04 +0200 Subject: [PATCH 069/111] Minor fixes --- sound/outputs/openal_out.cpp | 2 +- stream/clients/audiere_file.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 81e222362..c95c02ff6 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -146,7 +146,7 @@ SoundPtr OpenAL_Sound::clone() const // Constructor used for cloned sounds OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) - : refCnt(ref), bufferID(buf) + : bufferID(buf), refCnt(ref) { // Increase the reference count assert(ref != NULL); diff --git a/stream/clients/audiere_file.cpp b/stream/clients/audiere_file.cpp index 1d8e5b706..16bc7891a 100644 --- a/stream/clients/audiere_file.cpp +++ b/stream/clients/audiere_file.cpp @@ -8,7 +8,7 @@ bool AudiereFile::seek(int pos, SeekMode mode) assert(inp->isSeekable); assert(inp->hasPosition); - int newPos; + size_t newPos; switch(mode) { From 86a811c736810ef156fd2788be74951c5416ca03 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 12 Aug 2010 16:50:20 +0200 Subject: [PATCH 070/111] Another minor fix --- sound/clients/ogre_output_updater.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/clients/ogre_output_updater.hpp b/sound/clients/ogre_output_updater.hpp index 5072e9f1e..b73168c75 100644 --- a/sound/clients/ogre_output_updater.hpp +++ b/sound/clients/ogre_output_updater.hpp @@ -14,15 +14,15 @@ namespace Sound { struct OgreOutputUpdater : Ogre::FrameListener { - Mangle::Sound::SoundFactory &driver; + Mangle::Sound::SoundFactoryPtr driver; - OgreOutputUpdater(Mangle::Sound::SoundFactory &drv) + OgreOutputUpdater(Mangle::Sound::SoundFactoryPtr drv) : driver(drv) - { assert(drv.needsUpdate); } + { assert(drv->needsUpdate); } bool frameStarted(const Ogre::FrameEvent &evt) { - driver.update(); + driver->update(); return true; } }; From f3fa05ba2f596c21611dccc9d4b0b21c6a133792 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 14:11:05 +0200 Subject: [PATCH 071/111] Added a couple more OpenAL error checks --- sound/outputs/openal_out.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index c95c02ff6..acefb260c 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -11,11 +11,23 @@ using namespace Mangle::Sound; static void fail(const std::string &msg) { throw str_exception("OpenAL exception: " + msg); } -static void checkALError(const std::string &msg) +/* + Check for AL error. Since we're always calling this with string + literals, and it only makes sense to optimize for the non-error + case, the parameter is const char* rather than std::string. + + This way we don't force the compiler to create a string object each + time we're called (since the string is never used unless there's an + error), although a good compiler might have optimized that away in + any case. + */ +static void checkALError(const char *where) { ALenum err = alGetError(); if(err != AL_NO_ERROR) { + std::string msg = where; + const ALchar* errmsg = alGetString(err); if(errmsg) fail("\"" + std::string(alGetString(err)) + "\" while " + msg); @@ -154,7 +166,9 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) // Create a source alGenSources(1, &inst); + checkALError("creating instance (clone)"); alSourcei(inst, AL_BUFFER, bufferID); + checkALError("assigning buffer (clone)"); } // Constructor used for original (non-cloned) sounds @@ -166,6 +180,7 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) // Set up the OpenAL buffer alGenBuffers(1, &bufferID); + checkALError("generating buffer"); assert(bufferID != 0); // Does the stream support pointer operations? @@ -187,7 +202,9 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) // Create a source alGenSources(1, &inst); + checkALError("creating source"); alSourcei(inst, AL_BUFFER, bufferID); + checkALError("assigning buffer"); // Create a cheap reference counter for the buffer refCnt = new int; @@ -208,6 +225,7 @@ OpenAL_Sound::~OpenAL_Sound() // We're the last owner. Delete the buffer and the counter // itself. alDeleteBuffers(1, &bufferID); + checkALError("deleting buffer"); delete refCnt; } } From f95ea1677cc5c52790e1342aa6d3d6bba950b8be Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 14:43:13 +0200 Subject: [PATCH 072/111] Compiler warning fix --- sound/sources/stream_source.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/sources/stream_source.hpp b/sound/sources/stream_source.hpp index e4863545e..43c605a00 100644 --- a/sound/sources/stream_source.hpp +++ b/sound/sources/stream_source.hpp @@ -9,8 +9,8 @@ namespace Sound { /// A class for reading raw samples directly from a stream. class Stream2Samples : public SampleSource { - int32_t rate, channels, bits; Mangle::Stream::StreamPtr inp; + int32_t rate, channels, bits; public: Stream2Samples(Mangle::Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits) From 1ba1998223ad88ce89018e7b72adc7c0560bf2d6 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 16:12:57 +0200 Subject: [PATCH 073/111] Added niceified openal+ffmpeg handler, with test --- sound/filters/openal_audiere.hpp | 4 +- sound/filters/openal_ffmpeg.hpp | 23 ++++++++++ sound/tests/Makefile | 5 ++- sound/tests/openal_ffmpeg_test.cpp | 52 +++++++++++++++++++++++ sound/tests/output/openal_ffmpeg_test.out | 2 + 5 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 sound/filters/openal_ffmpeg.hpp create mode 100644 sound/tests/openal_ffmpeg_test.cpp create mode 100644 sound/tests/output/openal_ffmpeg_test.out diff --git a/sound/filters/openal_audiere.hpp b/sound/filters/openal_audiere.hpp index 5b62bd11b..5b9b51824 100644 --- a/sound/filters/openal_audiere.hpp +++ b/sound/filters/openal_audiere.hpp @@ -1,5 +1,5 @@ -#ifndef MANGLE_FFMPEG_OPENAL_H -#define MANGLE_FFMPEG_OPENAL_H +#ifndef MANGLE_AUDIERE_OPENAL_H +#define MANGLE_AUDIERE_OPENAL_H #include "input_filter.hpp" #include "../sources/audiere_source.hpp" diff --git a/sound/filters/openal_ffmpeg.hpp b/sound/filters/openal_ffmpeg.hpp new file mode 100644 index 000000000..42c76af0c --- /dev/null +++ b/sound/filters/openal_ffmpeg.hpp @@ -0,0 +1,23 @@ +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H + +#include "input_filter.hpp" +#include "../sources/ffmpeg_source.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds ffmpeg decoding to OpenAL. +class OpenAL_FFMpeg_Factory : public InputFilter +{ + public: + OpenAL_FFMpeg_Factory() + { + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new FFMpegLoader)); + } +}; + +}} +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 79c2ecaf8..a23f18025 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) @@ -9,6 +9,9 @@ L_AUDIERE=-laudiere openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) +openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) + openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) diff --git a/sound/tests/openal_ffmpeg_test.cpp b/sound/tests/openal_ffmpeg_test.cpp new file mode 100644 index 000000000..d4b8e9300 --- /dev/null +++ b/sound/tests/openal_ffmpeg_test.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_ffmpeg.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_FFMpeg_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main() +{ + play("cow.wav"); + play("owl.ogg"); + play("cow.wav", true); + return 0; +} diff --git a/sound/tests/output/openal_ffmpeg_test.out b/sound/tests/output/openal_ffmpeg_test.out new file mode 100644 index 000000000..96e1db0f9 --- /dev/null +++ b/sound/tests/output/openal_ffmpeg_test.out @@ -0,0 +1,2 @@ +Playing cow.wav +Playing owl.ogg From 160e8655d2503ad93992200d6d131186ede22aa3 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 16:52:29 +0200 Subject: [PATCH 074/111] Even more -Wall fixes - and added -Wall to test makefiles to actually catch them... --- input/tests/Makefile | 2 +- rend2d/tests/Makefile | 2 +- sound/sources/ffmpeg_source.cpp | 9 +++++---- sound/sources/ffmpeg_source.hpp | 2 +- sound/tests/Makefile | 2 +- stream/tests/Makefile | 2 +- vfs/clients/ogre_archive.hpp | 4 +++- vfs/tests/Makefile | 2 +- vfs/tests/dummy_test.cpp | 2 +- 9 files changed, 15 insertions(+), 12 deletions(-) diff --git a/input/tests/Makefile b/input/tests/Makefile index 8b7663dd5..8760adfe7 100644 --- a/input/tests/Makefile +++ b/input/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ +GCC=g++ -Wall all: sdl_driver_test ois_driver_test evtlist_test diff --git a/rend2d/tests/Makefile b/rend2d/tests/Makefile index c2e16c756..efbbda5d6 100644 --- a/rend2d/tests/Makefile +++ b/rend2d/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ +GCC=g++ -Wall all: sdl_test diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index bfb428234..2633515d8 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -97,8 +97,8 @@ size_t FFMpegSource::read(void *data, size_t length) // First, copy over any stored data we might be sitting on { - int s = storage.size(); - int copy = s; + size_t s = storage.size(); + size_t copy = s; if(s) { // Make sure there's room @@ -111,6 +111,7 @@ size_t FFMpegSource::read(void *data, size_t length) left -= copy; // Is there anything left in the storage? + assert(s>= copy); s -= copy; if(s) { @@ -133,7 +134,7 @@ size_t FFMpegSource::read(void *data, size_t length) break; // We only allow one stream per file at the moment - assert(StreamNum == packet.stream_index); + assert((int)StreamNum == packet.stream_index); // Decode the packet int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; @@ -151,7 +152,7 @@ size_t FFMpegSource::read(void *data, size_t length) if(len > 0) { // copy = how many bytes do we copy now - int copy = len; + size_t copy = len; if(copy > left) copy = left; diff --git a/sound/sources/ffmpeg_source.hpp b/sound/sources/ffmpeg_source.hpp index f0b5342ac..5bb478d74 100644 --- a/sound/sources/ffmpeg_source.hpp +++ b/sound/sources/ffmpeg_source.hpp @@ -18,7 +18,7 @@ class FFMpegSource : public SampleSource { AVFormatContext *FmtCtx; AVCodecContext *CodecCtx; - int StreamNum; + unsigned int StreamNum; std::vector storage; diff --git a/sound/tests/Makefile b/sound/tests/Makefile index a23f18025..cb647db73 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ +GCC=g++ -I../ -Wall all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 924a252c3..e64b90076 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ +GCC=g++ -I../ -Wall all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index 77191881a..7cba6cfc8 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -22,7 +22,9 @@ class MangleArchive : public Ogre::Archive public: MangleArchive(VFSPtr _vfs, const std::string &name, const std::string &archType = "Mangle") - : vfs(_vfs), Ogre::Archive(name, archType) {} + : Ogre::Archive(name, archType) + , vfs(_vfs) + {} bool isCaseSensitive() const { return vfs->isCaseSensitive; } diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index e0577b64e..a5d1d1712 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ +GCC=g++ -I../ -Wall all: dummy_test ogre_client_test ogre_resource_test ogre_server_test physfs_server_test diff --git a/vfs/tests/dummy_test.cpp b/vfs/tests/dummy_test.cpp index 8d7fc7dd6..541010c6a 100644 --- a/vfs/tests/dummy_test.cpp +++ b/vfs/tests/dummy_test.cpp @@ -17,7 +17,7 @@ void print(FileInfoPtr inf) { print(*inf); } void print(FileInfoList &lst) { - for(int i=0; i Date: Fri, 13 Aug 2010 19:27:45 +0200 Subject: [PATCH 075/111] Attempting to work around ffmpeg's inconsistent include path nonsense. --- sound/sources/ffmpeg_source.hpp | 4 ++-- sound/tests/Makefile | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/sources/ffmpeg_source.hpp b/sound/sources/ffmpeg_source.hpp index 5bb478d74..d422b9809 100644 --- a/sound/sources/ffmpeg_source.hpp +++ b/sound/sources/ffmpeg_source.hpp @@ -7,8 +7,8 @@ extern "C" { -#include -#include +#include +#include } namespace Mangle { diff --git a/sound/tests/Makefile b/sound/tests/Makefile index cb647db73..39b17be18 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -3,6 +3,7 @@ GCC=g++ -I../ -Wall all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere @@ -10,7 +11,7 @@ openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../ou $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp - $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) + $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG) openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) @@ -19,7 +20,7 @@ audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../.. $(GCC) $^ -o $@ $(L_AUDIERE) ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp - $(GCC) $^ -o $@ $(L_FFMPEG) + $(GCC) $^ -o $@ $(L_FFMPEG) $(I_FFMPEG) clean: rm *_test From 5b8e8d6b48847cd99d14064e8c7a1115efdd4fed Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 21:36:05 +0200 Subject: [PATCH 076/111] Added mpg123 sound source --- sound/filters/openal_mpg123.hpp | 24 ++++++ sound/sources/mpg123_source.cpp | 121 +++++++++++++++++++++++++++++ sound/sources/mpg123_source.hpp | 47 +++++++++++ sound/tests/Makefile | 5 +- sound/tests/openal_mpg123_test.cpp | 53 +++++++++++++ 5 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 sound/filters/openal_mpg123.hpp create mode 100644 sound/sources/mpg123_source.cpp create mode 100644 sound/sources/mpg123_source.hpp create mode 100644 sound/tests/openal_mpg123_test.cpp diff --git a/sound/filters/openal_mpg123.hpp b/sound/filters/openal_mpg123.hpp new file mode 100644 index 000000000..bfd926c0b --- /dev/null +++ b/sound/filters/openal_mpg123.hpp @@ -0,0 +1,24 @@ +#ifndef MANGLE_MPG123_OPENAL_H +#define MANGLE_MPG123_OPENAL_H + +#include "input_filter.hpp" +#include "../sources/mpg123_source.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds mpg123 decoding to OpenAL. Only supports +/// MP3 files. +class OpenAL_Mpg123_Factory : public InputFilter +{ + public: + OpenAL_Mpg123_Factory() + { + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new Mpg123Loader)); + } +}; + +}} +#endif diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp new file mode 100644 index 000000000..3bf56a468 --- /dev/null +++ b/sound/sources/mpg123_source.cpp @@ -0,0 +1,121 @@ +#include "mpg123_source.hpp" + +#include "../../tools/str_exception.hpp" + +#include + +using namespace Mangle::Stream; + +/* + TODOs: + + - mpg123 impressively enough supports custom stream reading. Which + means we could (and SHOULD!) support reading from Mangle::Streams + as well. But I'll save it til I need it. + + An alternative way to do this is through feeding (which they also + support), but that's more messy. + + - the library also supports output, via various other sources, + including ALSO, OSS, PortAudio, PulseAudio and SDL. Using this + library as a pure output library (if that is possible) would be a + nice shortcut over using those libraries - OTOH it's another + dependency. But it also means we could scavenge the mpg123 source + for these parts if we want them. + + - we could implement seek(), tell() and size(), but they aren't + really necessary. Further more, since the returned size is only a + guess, it is not safe to rely on it. + */ + +static void fail(const std::string &msg) +{ throw str_exception("Mangle::Mpg123 exception: " + msg); } + +static void checkError(int err, void *mh = NULL) +{ + if(err != MPG123_OK) + { + std::string msg; + if(mh) msg = mpg123_strerror((mpg123_handle*)mh); + else msg = mpg123_plain_strerror(err); + fail(msg); + } +} + +using namespace Mangle::Sound; + +void Mpg123Source::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits) +{ + // Use the values we found in the constructor + *pRate = rate; + *pChannels = channels; + *pBits = bits; +} + +size_t Mpg123Source::read(void *data, size_t length) +{ + size_t done; + // This is extraordinarily nice. I like this library. + int err = mpg123_read((mpg123_handle*)mh, (unsigned char*)data, length, &done); + assert(done <= length); + if(err == MPG123_DONE) + isEof = true; + else + checkError(err, mh); + return done; +} + +Mpg123Loader::Mpg123Loader(bool setup) +{ + // Do as we're told + if(setup) + { + int err = mpg123_init(); + checkError(err); + } + didSetup = setup; +} + +Mpg123Loader::~Mpg123Loader() +{ + // Deinitialize the library on exit + if(didSetup) + mpg123_exit(); +} + +Mpg123Source::Mpg123Source(const std::string &file) +{ + int err; + + // Create a new handle + mh = mpg123_new(NULL, &err); + if(mh == NULL) + checkError(err, mh); + + mpg123_handle *mhh = (mpg123_handle*)mh; + + // Open the file + err = mpg123_open(mhh, file.c_str()); + checkError(err, mh); + + // Get the format + int encoding; + err = mpg123_getformat(mhh, &rate, &channels, &encoding); + checkError(err, mh); + if(encoding != MPG123_ENC_SIGNED_16) + fail("Bad encoding"); + + // This is the only bit size we support. + bits = 16; + + // Ensure the output format does not change. (The tutorial on the + // mpg123 site did this, I assume it's kosher.) + mpg123_format_none(mhh); + mpg123_format(mhh,rate,channels,encoding); +} + +Mpg123Source::~Mpg123Source() +{ + mpg123_close((mpg123_handle*)mh); + mpg123_delete((mpg123_handle*)mh); +} diff --git a/sound/sources/mpg123_source.hpp b/sound/sources/mpg123_source.hpp new file mode 100644 index 000000000..1ac16b530 --- /dev/null +++ b/sound/sources/mpg123_source.hpp @@ -0,0 +1,47 @@ +#ifndef MANGLE_SOUND_MPG123_SOURCE_H +#define MANGLE_SOUND_MPG123_SOURCE_H + +#include "../source.hpp" +#include + +namespace Mangle { +namespace Sound { + +/// A sample source that decodes files using libmpg123. Only supports +/// MP3 files. +class Mpg123Source : public SampleSource +{ + void *mh; + long int rate; + int channels, bits; + + public: + /// Decode the given sound file + Mpg123Source(const std::string &file); + + /// Needed by SSL_Template but not yet supported + Mpg123Source(Mangle::Stream::StreamPtr data) + { assert(0); } + + ~Mpg123Source(); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); +}; + +#include "loadertemplate.hpp" + +/// A factory that loads Mpg123Sources from file and stream +struct Mpg123Loader : SSL_Template +{ + /** Sets up libmpg123 for you, and closes it on destruction. If you + want to do this yourself, send setup=false. + */ + Mpg123Loader(bool setup=true); + ~Mpg123Loader(); +private: + bool didSetup; +}; + +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 39b17be18..1c6c1fda5 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -Wall -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat @@ -13,6 +13,9 @@ openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../ou openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG) +openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ -lmpg123 ${L_OPENAL} + openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) diff --git a/sound/tests/openal_mpg123_test.cpp b/sound/tests/openal_mpg123_test.cpp new file mode 100644 index 000000000..a2826b755 --- /dev/null +++ b/sound/tests/openal_mpg123_test.cpp @@ -0,0 +1,53 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_mpg123.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_Mpg123_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main(int argc, char**argv) +{ + if(argc != 2) + cout << "Please specify an MP3 file\n"; + else + play(argv[1]); + return 0; +} From 85fa6d392319e72cbe359c55c7651faafcebdd2b Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 22:59:56 +0200 Subject: [PATCH 077/111] Constness fix (or rather, hack) --- sound/sources/mpg123_source.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index 3bf56a468..26e3fd7f4 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -94,8 +94,8 @@ Mpg123Source::Mpg123Source(const std::string &file) mpg123_handle *mhh = (mpg123_handle*)mh; - // Open the file - err = mpg123_open(mhh, file.c_str()); + // Open the file (hack around constness) + err = mpg123_open(mhh, (char*)file.c_str()); checkError(err, mh); // Get the format From 49f0e4b75f375c75724b650ba86a2ff840970c71 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 14 Aug 2010 13:02:41 +0200 Subject: [PATCH 078/111] Added setPitch to Sound --- sound/filters/pure_filter.hpp | 1 + sound/output.hpp | 3 +++ sound/outputs/openal_out.cpp | 6 ++++++ sound/outputs/openal_out.hpp | 1 + 4 files changed, 11 insertions(+) diff --git a/sound/filters/pure_filter.hpp b/sound/filters/pure_filter.hpp index ffa49d35a..9d19ffd18 100644 --- a/sound/filters/pure_filter.hpp +++ b/sound/filters/pure_filter.hpp @@ -23,6 +23,7 @@ namespace Mangle void setPan(float f) { client->setPan(f); } void setPos(float x, float y, float z) { client->setPos(x,y,z); } + void setPitch(float p) { client->setPitch(p); } void setRepeat(bool b) { client->setRepeat(b); } void setStreaming(bool b) { client->setStreaming(b); } diff --git a/sound/output.hpp b/sound/output.hpp index 404dcf4f8..35182e58c 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -48,6 +48,9 @@ class Sound /// Set left/right pan. -1.0 is left, 0.0 is center and 1.0 is right. virtual void setPan(float) = 0; + /// Set pitch (1.0 is normal speed) + virtual void setPitch(float) = 0; + /// Set the position. May not work with all backends. virtual void setPos(float x, float y, float z) = 0; diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index acefb260c..363097c3e 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -146,6 +146,12 @@ void OpenAL_Sound::setPos(float x, float y, float z) checkALError("setting position"); } +void OpenAL_Sound::setPitch(float pitch) +{ + alSourcef(inst, AL_PITCH, pitch); + checkALError("setting pitch"); +} + void OpenAL_Sound::setRepeat(bool rep) { alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE); diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index 18f32f2a9..084128e89 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -36,6 +36,7 @@ class OpenAL_Sound : public Sound bool isPlaying() const; void setVolume(float); void setPos(float x, float y, float z); + void setPitch(float); void setRepeat(bool); void setStreaming(bool) {} // Not implemented yet SoundPtr clone() const; From 3db61c8bdde65910e43a3a06b34738296960e9e8 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 14 Aug 2010 18:50:42 +0200 Subject: [PATCH 079/111] Added sound range function --- sound/filters/pure_filter.hpp | 2 ++ sound/output.hpp | 4 ++++ sound/outputs/openal_out.cpp | 7 +++++++ sound/outputs/openal_out.hpp | 5 +++++ sound/tests/output/openal_mpg123_test.out | 1 + 5 files changed, 19 insertions(+) create mode 100644 sound/tests/output/openal_mpg123_test.out diff --git a/sound/filters/pure_filter.hpp b/sound/filters/pure_filter.hpp index 9d19ffd18..1e8b9f92c 100644 --- a/sound/filters/pure_filter.hpp +++ b/sound/filters/pure_filter.hpp @@ -25,6 +25,8 @@ namespace Mangle { client->setPos(x,y,z); } void setPitch(float p) { client->setPitch(p); } void setRepeat(bool b) { client->setRepeat(b); } + void setRange(float a, float b=0, float c=0) + { client->setRange(a,b,c); } void setStreaming(bool b) { client->setStreaming(b); } // The clone() function is not implemented here, as you will diff --git a/sound/output.hpp b/sound/output.hpp index 35182e58c..596e08c58 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -51,6 +51,10 @@ class Sound /// Set pitch (1.0 is normal speed) virtual void setPitch(float) = 0; + /// Set range factors for 3D sounds. The meaning of the fields + /// depend on implementation. + virtual void setRange(float a, float b=0.0, float c=0.0) = 0; + /// Set the position. May not work with all backends. virtual void setPos(float x, float y, float z) = 0; diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 363097c3e..f0d5b1c0e 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -140,6 +140,13 @@ void OpenAL_Sound::setVolume(float volume) checkALError("setting volume"); } +void OpenAL_Sound::setRange(float a, float b, float) +{ + alSourcef(inst, AL_REFERENCE_DISTANCE, a); + alSourcef(inst, AL_MAX_DISTANCE, b); + checkALError("setting sound ranges"); +} + void OpenAL_Sound::setPos(float x, float y, float z) { alSource3f(inst, AL_POSITION, x, y, z); diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index 084128e89..c22be0de7 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -41,6 +41,11 @@ class OpenAL_Sound : public Sound void setStreaming(bool) {} // Not implemented yet SoundPtr clone() const; + // a = AL_REFERENCE_DISTANCE + // b = AL_MAX_DISTANCE + // c = ignored + void setRange(float a, float b=0.0, float c=0.0); + /// Not implemented void setPan(float) {} }; diff --git a/sound/tests/output/openal_mpg123_test.out b/sound/tests/output/openal_mpg123_test.out new file mode 100644 index 000000000..e55dabbb1 --- /dev/null +++ b/sound/tests/output/openal_mpg123_test.out @@ -0,0 +1 @@ +Please specify an MP3 file From a69938364fdbabee5c2c56248906a59225e43cff Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 14:13:13 +0200 Subject: [PATCH 080/111] Rewrote audiere to use new sample_reader --- sound/sources/audiere_source.cpp | 77 +++----------------------------- sound/sources/audiere_source.hpp | 21 +++------ sound/sources/sample_reader.cpp | 75 +++++++++++++++++++++++++++++++ sound/sources/sample_reader.hpp | 42 +++++++++++++++++ sound/tests/Makefile | 4 +- 5 files changed, 130 insertions(+), 89 deletions(-) create mode 100644 sound/sources/sample_reader.cpp create mode 100644 sound/sources/sample_reader.hpp diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 6fde40c07..1f11272b0 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -27,65 +27,6 @@ void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) } } -/* - Get data. Since Audiere operates with frames, not bytes, there's a - little conversion magic going on here. We need to make sure we're - reading a whole number of frames - if not, we need to store the - remainding part of the last frame and remember it for the next read - operation. - */ -size_t AudiereSource::read(void *_data, size_t length) -{ - if(isEof) return 0; - - char *data = (char*)_data; - - // Move the remains from the last operation first - if(pullSize) - { - // pullSize is how much was stored the last time, so skip that. - memcpy(data, pullOver+pullSize, PSIZE-pullSize); - length -= pullSize; - data += pullSize; - } - - // Determine the overshoot up front - pullSize = length % frameSize; - - // Number of whole frames - int frames = length / frameSize; - - // Read the data - int res = sample->read(frames, data); - - if(res < frames) - isEof = true; - - // Are we missing data? If we're at the end of the stream, then this - // doesn't apply. - if(!isEof && pullSize) - { - // Read one more sample - if(sample->read(1, pullOver) != 0) - { - // Then, move as much of it as we can fit into the output - // data - memcpy(data+length-pullSize, pullOver, pullSize); - } - else - // Failed reading, we're out of data - isEof = true; - } - - // If we're at the end of the stream, then no data remains to be - // pulled over - if(isEof) - pullSize = 0; - - // Return the total number of bytes stored - return frameSize*res + pullSize; -} - // --- Constructors --- AudiereSource::AudiereSource(const std::string &file) @@ -95,7 +36,7 @@ AudiereSource::AudiereSource(const std::string &file) if(!sample) fail("Couldn't load file " + file); - setup(); + doSetup(); } AudiereSource::AudiereSource(StreamPtr input) @@ -106,15 +47,15 @@ AudiereSource::AudiereSource(StreamPtr input) if(!sample) fail("Couldn't load stream"); - setup(); + doSetup(); } AudiereSource::AudiereSource(audiere::SampleSourcePtr src) : sample(src) -{ assert(sample); setup(); } +{ assert(sample); doSetup(); } // Common function called from all constructors -void AudiereSource::setup() +void AudiereSource::doSetup() { assert(sample); @@ -122,14 +63,8 @@ void AudiereSource::setup() int channels, rate; sample->getFormat(channels, rate, fmt); - pullSize = 0; - - // Calculate the size of one frame - frameSize = GetSampleSize(fmt) * channels; - - // Make sure that our pullover hack will work. Increase this size if - // this doesn't work in all cases. - assert(frameSize <= PSIZE); + // Calculate the size of one frame, and pass it to SampleReader. + setup(GetSampleSize(fmt) * channels); isSeekable = sample->isSeekable(); hasPosition = true; diff --git a/sound/sources/audiere_source.hpp b/sound/sources/audiere_source.hpp index 476546af3..d0189b710 100644 --- a/sound/sources/audiere_source.hpp +++ b/sound/sources/audiere_source.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_AUDIERE_SOURCE_H #define MANGLE_SOUND_AUDIERE_SOURCE_H -#include "../source.hpp" +#include "sample_reader.hpp" #include @@ -9,24 +9,14 @@ namespace Mangle { namespace Sound { /// A sample source that decodes files using Audiere -class AudiereSource : public SampleSource +class AudiereSource : public SampleReader { audiere::SampleSourcePtr sample; - // Number of bytes we cache between reads. This should correspond to - // the maximum possible value of frameSize. - static const int PSIZE = 10; + size_t readSamples(void *data, size_t length) + { return sample->read(length, data); } - // Size of one frame, in bytes - int frameSize; - - // Temporary storage for unevenly read samples. See the comment for - // read() in the .cpp file. - char pullOver[PSIZE]; - // How much of the above buffer is in use - int pullSize; - - void setup(); + void doSetup(); public: /// Decode the given sound file @@ -39,7 +29,6 @@ class AudiereSource : public SampleSource AudiereSource(audiere::SampleSourcePtr src); void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); - size_t read(void *data, size_t length); void seek(size_t pos) { sample->setPosition(pos/frameSize); } size_t tell() const { return sample->getPosition()*frameSize; } diff --git a/sound/sources/sample_reader.cpp b/sound/sources/sample_reader.cpp new file mode 100644 index 000000000..7cf5b0e00 --- /dev/null +++ b/sound/sources/sample_reader.cpp @@ -0,0 +1,75 @@ +#include "sample_reader.hpp" + +#include + +using namespace Mangle::Sound; + +void SampleReader::setup(int size) +{ + pullSize = 0; + frameSize = size; + pullOver = new char[size]; +} + +SampleReader::~SampleReader() +{ + if(pullOver) + delete[] pullOver; +} + +size_t SampleReader::read(void *_data, size_t length) +{ + if(isEof) return 0; + char *data = (char*)_data; + + // Move the remains from the last operation first + if(pullSize) + { + // pullSize is how much was stored the last time. The data is + // stored at the end of the buffer. + memcpy(data, pullOver+(frameSize-pullSize), pullSize); + length -= pullSize; + data += pullSize; + pullSize = 0; + } + + // Number of whole frames + size_t frames = length / frameSize; + + // Read the data + size_t res = readSamples(data, frames); + + // Total bytes read + size_t num = res*frameSize; + data += num; + + if(res < frames) + { + // End of stream. + isEof = true; + // Determine how much we read + return data-(char*)_data; + } + + // Determine the overshoot + pullSize = length - num; + + // Are we missing data? + if(pullSize) + { + // Fill in one sample + res = readSamples(pullOver,1); + if(res) + { + // Move as much as we can into the output buffer + memcpy(data, pullOver, pullSize); + data += pullSize; + } + else + // Failed reading, we're out of data + isEof = true; + } + + // Return the total number of bytes stored + return data-(char*)_data; +} diff --git a/sound/sources/sample_reader.hpp b/sound/sources/sample_reader.hpp new file mode 100644 index 000000000..16b98bee3 --- /dev/null +++ b/sound/sources/sample_reader.hpp @@ -0,0 +1,42 @@ +#ifndef MANGLE_SOUND_SAMPLE_READER_H +#define MANGLE_SOUND_SAMPLE_READER_H + +#include "../source.hpp" + +namespace Mangle { +namespace Sound { + + /* This is a helper base class for other SampleSource + implementations. Certain sources (like Audiere and libsndfile) + insist on reading whole samples rather than bytes. This class + compensates for that, and allows you to read bytes rather than + samples. + */ +class SampleReader : public SampleSource +{ + // Pullover buffer + char* pullOver; + + // How much of the above buffer is in use. + int pullSize; + +protected: + // Size of one frame, in bytes. This is also the size of the + // pullOver buffer. + int frameSize; + + // MUST be called by base class constructor. The parameter gives the + // size of one sample/frame, in bytes. + void setup(int); + + // Read the given number of samples, in multiples of frameSize. Does + // not have to set or respect isEof. + virtual size_t readSamples(void *data, size_t num) = 0; + + public: + SampleReader() : pullOver(NULL) {} + ~SampleReader(); + size_t read(void *data, size_t length); +}; +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 1c6c1fda5..0ad946fa9 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -7,7 +7,7 @@ I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp +openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp @@ -19,7 +19,7 @@ openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outpu openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) -audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp +audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp ../sources/sample_reader.cpp $(GCC) $^ -o $@ $(L_AUDIERE) ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp From ddfbcecfcd08b8739d0c356268242490935cec24 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 14:42:23 +0200 Subject: [PATCH 081/111] Added support for libsndfile (sound input) --- sound/filters/openal_sndfile.hpp | 24 ++++++++++ sound/sources/libsndfile.cpp | 50 +++++++++++++++++++++ sound/sources/libsndfile.hpp | 36 +++++++++++++++ sound/tests/Makefile | 5 ++- sound/tests/openal_sndfile_test.cpp | 52 ++++++++++++++++++++++ sound/tests/output/openal_sndfile_test.out | 2 + 6 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 sound/filters/openal_sndfile.hpp create mode 100644 sound/sources/libsndfile.cpp create mode 100644 sound/sources/libsndfile.hpp create mode 100644 sound/tests/openal_sndfile_test.cpp create mode 100644 sound/tests/output/openal_sndfile_test.out diff --git a/sound/filters/openal_sndfile.hpp b/sound/filters/openal_sndfile.hpp new file mode 100644 index 000000000..fd7e78025 --- /dev/null +++ b/sound/filters/openal_sndfile.hpp @@ -0,0 +1,24 @@ +#ifndef MANGLE_SNDFILE_OPENAL_H +#define MANGLE_SNDFILE_OPENAL_H + +#include "input_filter.hpp" +#include "../sources/libsndfile.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds libsnd decoding to OpenAL. libsndfile +/// supports most formats except MP3. +class OpenAL_SndFile_Factory : public InputFilter +{ + public: + OpenAL_SndFile_Factory() + { + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new SndFileLoader)); + } +}; + +}} +#endif diff --git a/sound/sources/libsndfile.cpp b/sound/sources/libsndfile.cpp new file mode 100644 index 000000000..502d8e5d1 --- /dev/null +++ b/sound/sources/libsndfile.cpp @@ -0,0 +1,50 @@ +#include "libsndfile.hpp" + +#include "../../tools/str_exception.hpp" +#include + +using namespace Mangle::Stream; + +static void fail(const std::string &msg) +{ throw str_exception("Mangle::libsndfile: " + msg); } + +using namespace Mangle::Sound; + +void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) +{ + *_rate = rate; + *_channels = channels; + *_bits = bits; +} + +size_t SndFileSource::readSamples(void *data, size_t length) +{ + // Read frames. We count channels as part of the frame. Even though + // libsndfile does not, since it still requires the number of frames + // read to be a multiple of channels. + return channels*sf_read_short((SNDFILE*)handle, (short*)data, length*channels); +} + +SndFileSource::SndFileSource(const std::string &file) +{ + SF_INFO info; + info.format = 0; + handle = sf_open(file.c_str(), SFM_READ, &info); + if(handle == NULL) + fail("Failed to open " + file); + + // I THINK that using sf_read_short forces the library to convert to + // 16 bits no matter what, but the libsndfile docs aren't exactly + // very clear on this point. + channels = info.channels; + rate = info.samplerate; + bits = 16; + + // 16 bits per sample times number of channels + setup(2*channels); +} + +SndFileSource::~SndFileSource() +{ + sf_close((SNDFILE*)handle); +} diff --git a/sound/sources/libsndfile.hpp b/sound/sources/libsndfile.hpp new file mode 100644 index 000000000..7286cf0fe --- /dev/null +++ b/sound/sources/libsndfile.hpp @@ -0,0 +1,36 @@ +#ifndef MANGLE_SOUND_SNDFILE_SOURCE_H +#define MANGLE_SOUND_SNDFILE_SOURCE_H + +#include "sample_reader.hpp" + +namespace Mangle { +namespace Sound { + +/// A sample source that decodes files using libsndfile. Supports most +/// formats except mp3. +class SndFileSource : public SampleReader +{ + void *handle; + int channels, rate, bits; + + size_t readSamples(void *data, size_t length); + + public: + /// Decode the given sound file + SndFileSource(const std::string &file); + + /// Decode the given sound stream (not supported) + SndFileSource(Mangle::Stream::StreamPtr src) { assert(0); } + + ~SndFileSource(); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); +}; + +#include "loadertemplate.hpp" + +/// A factory that loads SndFileSources from file and stream +typedef SSL_Template SndFileLoader; + +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 0ad946fa9..365e4a05c 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -Wall -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat @@ -16,6 +16,9 @@ openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outpu openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ -lmpg123 ${L_OPENAL} +openal_sndfile_test: openal_sndfile_test.cpp ../sources/libsndfile.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ -lsndfile ${L_OPENAL} + openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) diff --git a/sound/tests/openal_sndfile_test.cpp b/sound/tests/openal_sndfile_test.cpp new file mode 100644 index 000000000..bd5f117a5 --- /dev/null +++ b/sound/tests/openal_sndfile_test.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_sndfile.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_SndFile_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main() +{ + play("cow.wav"); + play("owl.ogg"); + play("cow.wav", true); + return 0; +} diff --git a/sound/tests/output/openal_sndfile_test.out b/sound/tests/output/openal_sndfile_test.out new file mode 100644 index 000000000..96e1db0f9 --- /dev/null +++ b/sound/tests/output/openal_sndfile_test.out @@ -0,0 +1,2 @@ +Playing cow.wav +Playing owl.ogg From b0ded6a3186ba541be0c759a27afe9a55e296b1f Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 16:26:24 +0200 Subject: [PATCH 082/111] Minor changes to sample_reader --- sound/sources/sample_reader.hpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sound/sources/sample_reader.hpp b/sound/sources/sample_reader.hpp index 16b98bee3..1cf9f5f68 100644 --- a/sound/sources/sample_reader.hpp +++ b/sound/sources/sample_reader.hpp @@ -11,22 +11,28 @@ namespace Sound { insist on reading whole samples rather than bytes. This class compensates for that, and allows you to read bytes rather than samples. + + There are two ways for subclasses to use this class. EITHER call + setup() with the size of frameSize. This will allocate a buffer, + which the destructor frees. OR set frameSize manually and + manipulate the pullOver pointer yourself. In that case you MUST + reset it to NULL if you don't want the destructor to call + delete[] on it. */ class SampleReader : public SampleSource { - // Pullover buffer - char* pullOver; - // How much of the above buffer is in use. int pullSize; protected: + // Pullover buffer + char* pullOver; + // Size of one frame, in bytes. This is also the size of the // pullOver buffer. int frameSize; - // MUST be called by base class constructor. The parameter gives the - // size of one sample/frame, in bytes. + // The parameter gives the size of one sample/frame, in bytes. void setup(int); // Read the given number of samples, in multiples of frameSize. Does @@ -34,7 +40,7 @@ protected: virtual size_t readSamples(void *data, size_t num) = 0; public: - SampleReader() : pullOver(NULL) {} + SampleReader() : pullOver(NULL), pullSize(0) {} ~SampleReader(); size_t read(void *data, size_t length); }; From 56ecc6585b2a362d90993f65d4d40507cc0ec40f Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 16:52:33 +0200 Subject: [PATCH 083/111] Added a manager for sndfile + mpg123, sorting on file type. --- sound/filters/openal_sndfile_mpg123.hpp | 33 +++++++++ sound/filters/source_splicer.hpp | 90 +++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 sound/filters/openal_sndfile_mpg123.hpp create mode 100644 sound/filters/source_splicer.hpp diff --git a/sound/filters/openal_sndfile_mpg123.hpp b/sound/filters/openal_sndfile_mpg123.hpp new file mode 100644 index 000000000..02600ee94 --- /dev/null +++ b/sound/filters/openal_sndfile_mpg123.hpp @@ -0,0 +1,33 @@ +#ifndef MANGLE_MPG123_OPENAL_H +#define MANGLE_MPG123_OPENAL_H + +#include "input_filter.hpp" +#include "source_splicer.hpp" +#include "../sources/mpg123_source.hpp" +#include "../sources/libsndfile.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that uses OpenAL for output, and mpg123 (for MP3) + +/// libsndfile (for everything else) to decode files. Can only load +/// from the file system, and uses the file name to differentiate +/// between mp3 and non-mp3 types. +class OpenAL_SndFile_Mpg123_Factory : public InputFilter +{ + public: + OpenAL_SndFile_Mpg123_Factory() + { + SourceSplicer *splice = new SourceSplicer; + + splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader)); + splice->setDefault(SampleSourceLoaderPtr(new SndFileLoader)); + + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(splice)); + } +}; + +}} +#endif diff --git a/sound/filters/source_splicer.hpp b/sound/filters/source_splicer.hpp new file mode 100644 index 000000000..69e8d024a --- /dev/null +++ b/sound/filters/source_splicer.hpp @@ -0,0 +1,90 @@ +#ifndef MANGLE_SOUND_SOURCE_SPLICE_H +#define MANGLE_SOUND_SOURCE_SPLICE_H + +#include "../source.hpp" +#include "../../tools/str_exception.hpp" +#include +#include +#include + +namespace Mangle +{ + namespace Sound + { + class SourceSplicer : public SampleSourceLoader + { + struct SourceType + { + std::string type; + SampleSourceLoaderPtr loader; + }; + + typedef std::list TypeList; + TypeList list; + SampleSourceLoaderPtr catchAll; + + static bool isMatch(char a, char b) + { + if(a >= 'A' && a <= 'Z') + a += 'a' - 'A'; + if(b >= 'A' && b <= 'Z') + b += 'a' - 'A'; + return a == b; + } + + public: + SourceSplicer() + { + canLoadStream = false; + canLoadFile = true; + } + + void add(const std::string &type, SampleSourceLoaderPtr fact) + { + SourceType tp; + tp.type = type; + tp.loader = fact; + list.push_back(tp); + } + + void setDefault(SampleSourceLoaderPtr def) + { + catchAll = def; + } + + SampleSourcePtr load(const std::string &file) + { + // Search the list for this file type. + for(TypeList::iterator it = list.begin(); + it != list.end(); it++) + { + const std::string &t = it->type; + + int diff = file.size() - t.size(); + if(diff < 0) continue; + + bool match = true; + for(int i=0; iloader->load(file); + } + // If not found, use the catch-all + if(catchAll) + return catchAll->load(file); + + throw str_exception("No handler for sound file " + file); + } + + SampleSourcePtr load(Stream::StreamPtr input) { assert(0); } + }; + } +} + +#endif From a0aafb9b1fb2462833fed205ab490bd84f073580 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 17:01:11 +0200 Subject: [PATCH 084/111] bleh --- sound/sources/sample_reader.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/sources/sample_reader.hpp b/sound/sources/sample_reader.hpp index 1cf9f5f68..89ddf1f65 100644 --- a/sound/sources/sample_reader.hpp +++ b/sound/sources/sample_reader.hpp @@ -40,7 +40,7 @@ protected: virtual size_t readSamples(void *data, size_t num) = 0; public: - SampleReader() : pullOver(NULL), pullSize(0) {} + SampleReader() : pullSize(0), pullOver(NULL) {} ~SampleReader(); size_t read(void *data, size_t length); }; From e8f01ae108c4ccc424801e4bee29e7836c5d0d67 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 17:04:44 +0200 Subject: [PATCH 085/111] Fixed include guard --- sound/filters/openal_sndfile_mpg123.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/filters/openal_sndfile_mpg123.hpp b/sound/filters/openal_sndfile_mpg123.hpp index 02600ee94..6e5db4d0e 100644 --- a/sound/filters/openal_sndfile_mpg123.hpp +++ b/sound/filters/openal_sndfile_mpg123.hpp @@ -1,5 +1,5 @@ -#ifndef MANGLE_MPG123_OPENAL_H -#define MANGLE_MPG123_OPENAL_H +#ifndef MANGLE_SNDFILE_MPG123_OPENAL_H +#define MANGLE_SNDFILE_MPG123_OPENAL_H #include "input_filter.hpp" #include "source_splicer.hpp" From c982f701cacdd2932bfdc22b168f54221a549b62 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 17:07:27 +0200 Subject: [PATCH 086/111] Minor fix again --- sound/filters/source_splicer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/filters/source_splicer.hpp b/sound/filters/source_splicer.hpp index 69e8d024a..9fd49d126 100644 --- a/sound/filters/source_splicer.hpp +++ b/sound/filters/source_splicer.hpp @@ -64,7 +64,7 @@ namespace Mangle if(diff < 0) continue; bool match = true; - for(int i=0; i Date: Tue, 17 Aug 2010 11:25:26 +0200 Subject: [PATCH 087/111] Moved OpenAL_Sound to cpp file, added delayed buffer creation (prelude for streaming) --- sound/output.hpp | 2 +- sound/outputs/openal_out.cpp | 129 ++++++++++++++++++++++++++--- sound/outputs/openal_out.hpp | 66 ++------------- sound/sources/mpg123_source.cpp | 7 +- sound/tests/openal_output_test.cpp | 16 +++- stream/filters/buffer_stream.hpp | 9 +- 6 files changed, 144 insertions(+), 85 deletions(-) diff --git a/sound/output.hpp b/sound/output.hpp index 596e08c58..a9012b958 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -72,7 +72,7 @@ class Sound /** Playback status is not cloned, only the sound data itself. Back-ends can use this as a means of sharing data and saving memory. */ - virtual SoundPtr clone() const = 0; + virtual SoundPtr clone() = 0; /// Virtual destructor virtual ~Sound() {} diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index f0d5b1c0e..a49840f4e 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -4,6 +4,9 @@ #include "../../stream/filters/buffer_stream.hpp" #include "../../tools/str_exception.hpp" +#include +#include + using namespace Mangle::Sound; // ---- Helper functions and classes ---- @@ -69,17 +72,91 @@ static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) fail("Unsupported input format"); } +/// OpenAL sound output +class OpenAL_Sound : public Sound +{ + ALuint inst; + ALuint bufferID; + + // Poor mans reference counting. Might improve this later. When + // NULL, the buffer has not been set up yet. + int *refCnt; + + bool streaming; + + // Input stream + SampleSourcePtr input; + + void setupBuffer(); + + public: + /// Read samples from the given input buffer + OpenAL_Sound(SampleSourcePtr input); + + /// Play an existing buffer, with a given ref counter. Used + /// internally for cloning. + OpenAL_Sound(ALuint buf, int *ref); + + ~OpenAL_Sound(); + + void play(); + void stop(); + void pause(); + bool isPlaying() const; + void setVolume(float); + void setPos(float x, float y, float z); + void setPitch(float); + void setRepeat(bool); + void setStreaming(bool s) { streaming = s; } + SoundPtr clone(); + + // a = AL_REFERENCE_DISTANCE + // b = AL_MAX_DISTANCE + // c = ignored + void setRange(float a, float b=0.0, float c=0.0); + + /// Not implemented + void setPan(float) {} +}; + // ---- OpenAL_Factory ---- +SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input) +{ + return SoundPtr(new OpenAL_Sound(input)); +} + +void OpenAL_Factory::update() +{ +} + +void OpenAL_Factory::setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) +{ + ALfloat orient[6]; + orient[0] = fx; + orient[1] = fy; + orient[2] = fz; + orient[3] = ux; + orient[4] = uy; + orient[5] = uz; + alListener3f(AL_POSITION, x, y, z); + alListenerfv(AL_ORIENTATION, orient); +} + OpenAL_Factory::OpenAL_Factory(bool doSetup) - : didSetup(doSetup) + : device(NULL), context(NULL), didSetup(doSetup) { - needsUpdate = false; + needsUpdate = true; has3D = true; canLoadFile = false; canLoadStream = false; canLoadSource = true; + ALCdevice *Device; + ALCcontext *Context; + if(doSetup) { // Set up sound system @@ -90,6 +167,9 @@ OpenAL_Factory::OpenAL_Factory(bool doSetup) fail("Failed to initialize context or device"); alcMakeContextCurrent(Context); + + device = Device; + context = Context; } } @@ -99,8 +179,8 @@ OpenAL_Factory::~OpenAL_Factory() if(didSetup) { alcMakeContextCurrent(NULL); - if(Context) alcDestroyContext(Context); - if(Device) alcCloseDevice(Device); + if(context) alcDestroyContext((ALCcontext*)context); + if(device) alcCloseDevice((ALCdevice*)device); } } @@ -108,6 +188,7 @@ OpenAL_Factory::~OpenAL_Factory() void OpenAL_Sound::play() { + setupBuffer(); alSourcePlay(inst); checkALError("starting playback"); } @@ -164,14 +245,16 @@ void OpenAL_Sound::setRepeat(bool rep) alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE); } -SoundPtr OpenAL_Sound::clone() const +SoundPtr OpenAL_Sound::clone() { + setupBuffer(); + assert(!streaming && "cloning streamed sounds not supported"); return SoundPtr(new OpenAL_Sound(bufferID, refCnt)); } // Constructor used for cloned sounds OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) - : bufferID(buf), refCnt(ref) + : bufferID(buf), refCnt(ref), streaming(false) { // Increase the reference count assert(ref != NULL); @@ -185,12 +268,35 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) } // Constructor used for original (non-cloned) sounds -OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) +OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input) + : bufferID(0), refCnt(NULL), streaming(false), input(_input) { + // Create a source + alGenSources(1, &inst); + checkALError("creating source"); + + // By default, the sound starts out in a buffer-less mode. We don't + // create a buffer until the sound is played. This gives the user + // the chance to call setStreaming(true) first. +} + +void OpenAL_Sound::setupBuffer() +{ + if(refCnt != NULL) return; + + assert(input); + // Get the format int fmt, rate; getALFormat(input, fmt, rate); + if(streaming) + { + // To be done + } + + // NON-STREAMING + // Set up the OpenAL buffer alGenBuffers(1, &bufferID); checkALError("generating buffer"); @@ -205,17 +311,16 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) else { // Read the entire stream into a temporary buffer first - Mangle::Stream::BufferStream buf(input); + Mangle::Stream::BufferStream buf(input, streaming?1024*1024:32*1024); // Then copy that into OpenAL alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate); } - checkALError("loading sound buffer"); - // Create a source - alGenSources(1, &inst); - checkALError("creating source"); + // We're done with the input stream, release the pointer + input.reset(); + alSourcei(inst, AL_BUFFER, bufferID); checkALError("assigning buffer"); diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index c22be0de7..f3828ff1b 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -3,57 +3,13 @@ #include "../output.hpp" -#include -#include -#include - namespace Mangle { namespace Sound { -/// OpenAL sound output -class OpenAL_Sound : public Sound -{ - protected: - ALuint inst; - ALuint bufferID; - - // Poor mans reference counting. Might improve this later. - int *refCnt; - - public: - /// Read samples from the given input buffer - OpenAL_Sound(SampleSourcePtr input); - - /// Play an existing buffer, with a given ref counter. Used - /// internally for cloning. - OpenAL_Sound(ALuint buf, int *ref); - - ~OpenAL_Sound(); - - void play(); - void stop(); - void pause(); - bool isPlaying() const; - void setVolume(float); - void setPos(float x, float y, float z); - void setPitch(float); - void setRepeat(bool); - void setStreaming(bool) {} // Not implemented yet - SoundPtr clone() const; - - // a = AL_REFERENCE_DISTANCE - // b = AL_MAX_DISTANCE - // c = ignored - void setRange(float a, float b=0.0, float c=0.0); - - /// Not implemented - void setPan(float) {} -}; - class OpenAL_Factory : public SoundFactory { - ALCdevice *Device; - ALCcontext *Context; + void *device; + void *context; bool didSetup; public: @@ -65,24 +21,12 @@ class OpenAL_Factory : public SoundFactory SoundPtr load(const std::string &file) { assert(0); } SoundPtr load(Stream::StreamPtr input) { assert(0); } - SoundPtr loadRaw(SampleSourcePtr input) - { return SoundPtr(new OpenAL_Sound(input)); } + SoundPtr loadRaw(SampleSourcePtr input); - void update() {} + void update(); void setListenerPos(float x, float y, float z, float fx, float fy, float fz, - float ux, float uy, float uz) - { - ALfloat orient[6]; - orient[0] = fx; - orient[1] = fy; - orient[2] = fz; - orient[3] = ux; - orient[4] = uy; - orient[5] = uz; - alListener3f(AL_POSITION, x, y, z); - alListenerfv(AL_ORIENTATION, orient); - } + float ux, float uy, float uz); }; }} // namespaces diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index 26e3fd7f4..327279b85 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -17,14 +17,13 @@ using namespace Mangle::Stream; support), but that's more messy. - the library also supports output, via various other sources, - including ALSO, OSS, PortAudio, PulseAudio and SDL. Using this + including ALSA, OSS, PortAudio, PulseAudio and SDL. Using this library as a pure output library (if that is possible) would be a nice shortcut over using those libraries - OTOH it's another - dependency. But it also means we could scavenge the mpg123 source - for these parts if we want them. + dependency. - we could implement seek(), tell() and size(), but they aren't - really necessary. Further more, since the returned size is only a + really necessary. Furthermore, since the returned size is only a guess, it is not safe to rely on it. */ diff --git a/sound/tests/openal_output_test.cpp b/sound/tests/openal_output_test.cpp index bc781c1a4..c5b99f56f 100644 --- a/sound/tests/openal_output_test.cpp +++ b/sound/tests/openal_output_test.cpp @@ -25,15 +25,23 @@ int main() cout << "Playing\n"; - // This initializes OpenAL for us, and serves no other purpose. OpenAL_Factory mg; - OpenAL_Sound snd(source); + SoundPtr snd = mg.loadRaw(source); + try { - snd.play(); + // Try setting all kinds of stuff before playing. OpenAL_Sound + // uses delayed buffer loading, but these should still work + // without a buffer. + snd->stop(); + snd->pause(); + snd->setVolume(0.8); + snd->setPitch(0.9); + + snd->play(); - while(snd.isPlaying()) + while(snd->isPlaying()) { usleep(10000); } diff --git a/stream/filters/buffer_stream.hpp b/stream/filters/buffer_stream.hpp index 63b70000e..f037212a3 100644 --- a/stream/filters/buffer_stream.hpp +++ b/stream/filters/buffer_stream.hpp @@ -16,7 +16,11 @@ class BufferStream : public MemoryStream std::vector buffer; public: - BufferStream(StreamPtr input) + /* + input = stream to copy + ADD = each read increment (for streams without size()) + */ + BufferStream(StreamPtr input, size_t ADD = 32*1024) { assert(input); @@ -37,7 +41,6 @@ class BufferStream : public MemoryStream { // We DON'T know how big the stream is. We'll have to read // it in increments. - const unsigned int ADD = 32*1024; size_t len=0, newlen; while(!input->eof()) @@ -52,7 +55,7 @@ class BufferStream : public MemoryStream // If we read less than expected, we should be at the // end of the stream - assert(read == ADD || input->eof()); + assert(read == ADD || (read < ADD && input->eof())); } // Downsize to match the real length From cd4ed4e6bfb23d736c4b1f30d6096ec164ba937b Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 17 Aug 2010 13:17:39 +0200 Subject: [PATCH 088/111] Implemented OpenAL streaming. Fixed bugs in SampleReader and elsewhere. --- sound/outputs/openal_out.cpp | 201 ++++++++++++++++++++++++----- sound/outputs/openal_out.hpp | 11 ++ sound/sources/libsndfile.cpp | 6 +- sound/sources/sample_reader.cpp | 38 +++++- sound/tests/openal_output_test.cpp | 5 + stream/servers/std_stream.hpp | 2 +- 6 files changed, 217 insertions(+), 46 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index a49840f4e..166b104e8 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -11,6 +11,20 @@ using namespace Mangle::Sound; // ---- Helper functions and classes ---- +// Static buffer used to shuffle sound data from the input into +// OpenAL. The data is only stored temporarily and then immediately +// shuffled off to the library. This is not thread safe, but it works +// fine with multiple sounds in one thread. It could be made thread +// safe simply by using thread local storage. +const size_t BSIZE = 32*1024; +static char tmp_buffer[BSIZE]; + +// Number of buffers used (per sound) for streaming sounds. Each +// buffer is of size BSIZE. Increasing this will make streaming sounds +// more fault tolerant against temporary lapses in call to update(), +// but will also increase memory usage. 4 should be ok. +const int STREAM_BUF_NUM = 4; + static void fail(const std::string &msg) { throw str_exception("OpenAL exception: " + msg); } @@ -73,10 +87,18 @@ static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) } /// OpenAL sound output -class OpenAL_Sound : public Sound +class Mangle::Sound::OpenAL_Sound : public Sound { ALuint inst; - ALuint bufferID; + + // Buffers. Only the first is used for non-streaming sounds. + ALuint bufferID[4]; + + // Number of buffers used + int bufNum; + + // Parameters used for filling buffers + int fmt, rate; // Poor mans reference counting. Might improve this later. When // NULL, the buffer has not been set up yet. @@ -87,18 +109,74 @@ class OpenAL_Sound : public Sound // Input stream SampleSourcePtr input; + OpenAL_Factory *owner; + bool ownerAlive; + + // Used for streamed sound list + OpenAL_Sound *next, *prev; + void setupBuffer(); + // Fill data into the given buffer and queue it, if there is any + // data left to queue. Assumes the buffer is already unqueued, if + // necessary. + void queueBuffer(ALuint buf) + { + // If there is no more data, do nothing + if(!input) return; + if(input->eof()) + { + input.reset(); + return; + } + + // Get some new data + size_t bytes = input->read(tmp_buffer, BSIZE); + if(bytes == 0) + { + input.reset(); + return; + } + + // Move data into the OpenAL buffer + alBufferData(buf, fmt, tmp_buffer, bytes, rate); + // Queue it + alSourceQueueBuffers(inst, 1, &buf); + checkALError("Queueing buffer data"); + } + public: /// Read samples from the given input buffer - OpenAL_Sound(SampleSourcePtr input); + OpenAL_Sound(SampleSourcePtr input, OpenAL_Factory *fact); /// Play an existing buffer, with a given ref counter. Used /// internally for cloning. - OpenAL_Sound(ALuint buf, int *ref); + OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact); ~OpenAL_Sound(); + // Must be called regularly on streamed sounds + void update() + { + if(!streaming) return; + if(!input) return; + + // Get the number of processed buffers + ALint count; + alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); + checkALError("getting number of unprocessed buffers"); + + for(int i=0; iupdate(); +} + +void OpenAL_Factory::notifyStreaming(OpenAL_Sound *snd) +{ + // Add the sound to the streaming list + streaming.push_back(snd); +} + +void OpenAL_Factory::notifyDelete(OpenAL_Sound *snd) +{ + // Remove the sound from the stream list + streaming.remove(snd); +} + OpenAL_Factory::~OpenAL_Factory() { + // Notify remaining streamed sounds that we're dying + StreamList::iterator it = streaming.begin(); + for(;it != streaming.end(); it++) + (*it)->notifyOwnerDeath(); + // Deinitialize sound system if(didSetup) { @@ -249,27 +355,31 @@ SoundPtr OpenAL_Sound::clone() { setupBuffer(); assert(!streaming && "cloning streamed sounds not supported"); - return SoundPtr(new OpenAL_Sound(bufferID, refCnt)); + return SoundPtr(new OpenAL_Sound(bufferID[0], refCnt, owner)); } // Constructor used for cloned sounds -OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) - : bufferID(buf), refCnt(ref), streaming(false) +OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact) + : refCnt(ref), streaming(false), owner(fact), ownerAlive(false) { // Increase the reference count assert(ref != NULL); *refCnt++; + // Set up buffer + bufferID[0] = buf; + bufNum = 1; + // Create a source alGenSources(1, &inst); checkALError("creating instance (clone)"); - alSourcei(inst, AL_BUFFER, bufferID); + alSourcei(inst, AL_BUFFER, bufferID[0]); checkALError("assigning buffer (clone)"); } // Constructor used for original (non-cloned) sounds -OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input) - : bufferID(0), refCnt(NULL), streaming(false), input(_input) +OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input, OpenAL_Factory *fact) + : refCnt(NULL), streaming(false), input(_input), owner(fact), ownerAlive(false) { // Create a source alGenSources(1, &inst); @@ -287,46 +397,60 @@ void OpenAL_Sound::setupBuffer() assert(input); // Get the format - int fmt, rate; getALFormat(input, fmt, rate); + // Create a cheap reference counter for the buffer + refCnt = new int; + *refCnt = 1; + + if(streaming) bufNum = STREAM_BUF_NUM; + else bufNum = 1; + + // Set up the OpenAL buffer(s) + alGenBuffers(bufNum, bufferID); + checkALError("generating buffer(s)"); + assert(bufferID[0] != 0); + + // STREAMING. if(streaming) { - // To be done - } + // Just queue all the buffers with data and exit. queueBuffer() + // will work correctly also in the case where there is not + // enough data to fill all the buffers. + for(int i=0; inotifyStreaming(this); + ownerAlive = true; - // NON-STREAMING + return; + } - // Set up the OpenAL buffer - alGenBuffers(1, &bufferID); - checkALError("generating buffer"); - assert(bufferID != 0); + // NON-STREAMING. We have to load all the data and shove it into the + // buffer. // Does the stream support pointer operations? if(input->hasPtr) { // If so, we can read the data directly from the stream - alBufferData(bufferID, fmt, input->getPtr(), input->size(), rate); + alBufferData(bufferID[0], fmt, input->getPtr(), input->size(), rate); } else { // Read the entire stream into a temporary buffer first - Mangle::Stream::BufferStream buf(input, streaming?1024*1024:32*1024); + Mangle::Stream::BufferStream buf(input, 128*1024); // Then copy that into OpenAL - alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate); + alBufferData(bufferID[0], fmt, buf.getPtr(), buf.size(), rate); } - checkALError("loading sound buffer"); + checkALError("loading sound data"); // We're done with the input stream, release the pointer input.reset(); - alSourcei(inst, AL_BUFFER, bufferID); + alSourcei(inst, AL_BUFFER, bufferID[0]); checkALError("assigning buffer"); - - // Create a cheap reference counter for the buffer - refCnt = new int; - *refCnt = 1; } OpenAL_Sound::~OpenAL_Sound() @@ -337,12 +461,19 @@ OpenAL_Sound::~OpenAL_Sound() // Return sound alDeleteSources(1, &inst); + // Notify the factory that we quit. You will hear from our union + // rep. The bool check is to handle cases where the manager goes out + // of scope before the sounds do. In that case, don't try to contact + // the factory. + if(ownerAlive) + owner->notifyDelete(this); + // Decrease the reference counter if((-- *refCnt) == 0) { - // We're the last owner. Delete the buffer and the counter + // We're the last owner. Delete the buffer(s) and the counter // itself. - alDeleteBuffers(1, &bufferID); + alDeleteBuffers(bufNum, bufferID); checkALError("deleting buffer"); delete refCnt; } diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index f3828ff1b..1460e4ccc 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -2,16 +2,27 @@ #define MANGLE_SOUND_OPENAL_OUT_H #include "../output.hpp" +#include namespace Mangle { namespace Sound { +class OpenAL_Sound; + class OpenAL_Factory : public SoundFactory { void *device; void *context; bool didSetup; + // List of streaming sounds that need to be updated every frame. + typedef std::list StreamList; + StreamList streaming; + + friend class OpenAL_Sound; + void notifyStreaming(OpenAL_Sound*); + void notifyDelete(OpenAL_Sound*); + public: /// Initialize object. Pass true (default) if you want the /// constructor to set up the current ALCdevice and ALCcontext for diff --git a/sound/sources/libsndfile.cpp b/sound/sources/libsndfile.cpp index 502d8e5d1..2e34264fb 100644 --- a/sound/sources/libsndfile.cpp +++ b/sound/sources/libsndfile.cpp @@ -19,9 +19,9 @@ void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) size_t SndFileSource::readSamples(void *data, size_t length) { - // Read frames. We count channels as part of the frame. Even though - // libsndfile does not, since it still requires the number of frames - // read to be a multiple of channels. + // Read frames. We count channels as part of the frame, even though + // libsndfile does not. This is because the library still requires + // the number of frames read to be a multiple of channels. return channels*sf_read_short((SNDFILE*)handle, (short*)data, length*channels); } diff --git a/sound/sources/sample_reader.cpp b/sound/sources/sample_reader.cpp index 7cf5b0e00..fb4be9d5a 100644 --- a/sound/sources/sample_reader.cpp +++ b/sound/sources/sample_reader.cpp @@ -22,15 +22,36 @@ size_t SampleReader::read(void *_data, size_t length) if(isEof) return 0; char *data = (char*)_data; - // Move the remains from the last operation first + // Pullsize holds the number of bytes that were copied "extra" at + // the end of LAST round. If non-zero, it also means there is data + // left in the pullOver buffer. if(pullSize) { - // pullSize is how much was stored the last time. The data is - // stored at the end of the buffer. - memcpy(data, pullOver+(frameSize-pullSize), pullSize); - length -= pullSize; - data += pullSize; - pullSize = 0; + // Amount of data left + size_t doRead = frameSize - pullSize; + assert(doRead > 0); + + // Make sure we don't read more than we're supposed to + if(doRead > length) doRead = length; + + memcpy(data, pullOver+pullSize, doRead); + + // Update the number of bytes now copied + pullSize += doRead; + assert(pullSize <= frameSize); + + if(pullSize < frameSize) + { + // There is STILL data left in the pull buffer, and we've + // done everything we were supposed to. Leave it and return. + assert(doRead == length); + return doRead; + } + + // Set up variables for further reading below. No need to update + // pullSize, it is overwritten anyway. + length -= doRead; + data += doRead; } // Number of whole frames @@ -38,6 +59,7 @@ size_t SampleReader::read(void *_data, size_t length) // Read the data size_t res = readSamples(data, frames); + assert(res <= frames); // Total bytes read size_t num = res*frameSize; @@ -53,12 +75,14 @@ size_t SampleReader::read(void *_data, size_t length) // Determine the overshoot pullSize = length - num; + assert(pullSize < frameSize && pullSize >= 0); // Are we missing data? if(pullSize) { // Fill in one sample res = readSamples(pullOver,1); + assert(res == 1 || res == 0); if(res) { // Move as much as we can into the output buffer diff --git a/sound/tests/openal_output_test.cpp b/sound/tests/openal_output_test.cpp index c5b99f56f..a8059ec65 100644 --- a/sound/tests/openal_output_test.cpp +++ b/sound/tests/openal_output_test.cpp @@ -39,11 +39,16 @@ int main() snd->setVolume(0.8); snd->setPitch(0.9); + // Also test streaming, since all the other examples test + // non-streaming sounds. + snd->setStreaming(true); + snd->play(); while(snd->isPlaying()) { usleep(10000); + mg.update(); } } catch(exception &e) diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index 448306e5c..971c95415 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -29,7 +29,7 @@ class StdStream : public Stream size_t read(void* buf, size_t len) { inf->read((char*)buf, len); - if(inf->fail()) + if(inf->bad()) fail("error reading from stream"); return inf->gcount(); } From 27bef840916581c0dd85b9c8b6ab4109e000dbac Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 17 Aug 2010 16:03:51 +0200 Subject: [PATCH 089/111] Tested if exchanging sf_read with sf_readf in libsndfile help OpenMW crash (it did not) --- sound/sources/libsndfile.cpp | 6 ++---- sound/sources/mpg123_source.cpp | 2 +- sound/tests/openal_mpg123_test.cpp | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/sources/libsndfile.cpp b/sound/sources/libsndfile.cpp index 2e34264fb..9ac7ee465 100644 --- a/sound/sources/libsndfile.cpp +++ b/sound/sources/libsndfile.cpp @@ -19,10 +19,8 @@ void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) size_t SndFileSource::readSamples(void *data, size_t length) { - // Read frames. We count channels as part of the frame, even though - // libsndfile does not. This is because the library still requires - // the number of frames read to be a multiple of channels. - return channels*sf_read_short((SNDFILE*)handle, (short*)data, length*channels); + // readf_* reads entire frames, including channels + return sf_readf_short((SNDFILE*)handle, (short*)data, length); } SndFileSource::SndFileSource(const std::string &file) diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index 327279b85..8a0dbd102 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -102,7 +102,7 @@ Mpg123Source::Mpg123Source(const std::string &file) err = mpg123_getformat(mhh, &rate, &channels, &encoding); checkError(err, mh); if(encoding != MPG123_ENC_SIGNED_16) - fail("Bad encoding"); + fail("Unsupported encoding in " + file); // This is the only bit size we support. bits = 16; diff --git a/sound/tests/openal_mpg123_test.cpp b/sound/tests/openal_mpg123_test.cpp index a2826b755..fef1a5605 100644 --- a/sound/tests/openal_mpg123_test.cpp +++ b/sound/tests/openal_mpg123_test.cpp @@ -29,6 +29,7 @@ void play(const char* name, bool stream=false) else snd = mg.load(name); + snd->setStreaming(true); snd->play(); while(snd->isPlaying()) From 8f154ac622e8d69cafe9bfecf5ab65494c443e6d Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 18 Aug 2010 12:32:38 +0200 Subject: [PATCH 090/111] Added custom WAV loader --- sound/filters/openal_various.hpp | 40 +++++++++ sound/sources/wav_source.cpp | 95 ++++++++++++++++++++++ sound/sources/wav_source.hpp | 49 +++++++++++ sound/tests/Makefile | 8 +- sound/tests/openal_various_test.cpp | 51 ++++++++++++ sound/tests/output/openal_various_test.out | 1 + sound/tests/output/wav_source_test.out | 12 +++ sound/tests/wav_source_test.cpp | 48 +++++++++++ 8 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 sound/filters/openal_various.hpp create mode 100644 sound/sources/wav_source.cpp create mode 100644 sound/sources/wav_source.hpp create mode 100644 sound/tests/openal_various_test.cpp create mode 100644 sound/tests/output/openal_various_test.out create mode 100644 sound/tests/output/wav_source_test.out create mode 100644 sound/tests/wav_source_test.cpp diff --git a/sound/filters/openal_various.hpp b/sound/filters/openal_various.hpp new file mode 100644 index 000000000..87030f4b6 --- /dev/null +++ b/sound/filters/openal_various.hpp @@ -0,0 +1,40 @@ +#ifndef MANGLE_VARIOUS_OPENAL_H +#define MANGLE_VARIOUS_OPENAL_H + +#include "input_filter.hpp" +#include "source_splicer.hpp" +#include "../sources/mpg123_source.hpp" +#include "../sources/wav_source.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/** A InputFilter that uses OpenAL for output, and load input from + various individual sources, depending on file extension. Currently + supports: + + MP3: mpg123 + WAV: custom wav loader (PCM only) + + This could be an alternative to using eg. libsndfile or other 3rd + party decoder libraries. (We implemented this for OpenMW because + we were experiencing crashes when using libsndfile.) + */ +class OpenAL_Various_Factory : public InputFilter +{ + public: + OpenAL_Various_Factory() + { + SourceSplicer *splice = new SourceSplicer; + + splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader)); + splice->add("wav", SampleSourceLoaderPtr(new WavLoader)); + + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(splice)); + } +}; + +}} +#endif diff --git a/sound/sources/wav_source.cpp b/sound/sources/wav_source.cpp new file mode 100644 index 000000000..5ee87d492 --- /dev/null +++ b/sound/sources/wav_source.cpp @@ -0,0 +1,95 @@ +#include "wav_source.hpp" + +#include "../../tools/str_exception.hpp" +#include "../../stream/servers/file_stream.hpp" + +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +static void fail(const std::string &msg) +{ throw str_exception("Mangle::Wav exception: " + msg); } + +void WavSource::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits) +{ + // Use the values we found in the constructor + *pRate = rate; + *pChannels = channels; + *pBits = bits; +} + +void WavSource::seek(size_t pos) +{ + // Seek the stream and set 'left' + assert(isSeekable); + if(pos > total) pos = total; + input->seek(dataOffset + pos); + left = total-pos; +} + +size_t WavSource::read(void *data, size_t length) +{ + if(length > left) + length = left; + input->read(data, length); + return length; +} + +void WavSource::open(Mangle::Stream::StreamPtr data) +{ + input = data; + + hasPosition = true; + hasSize = true; + // If we can check position and seek in the input stream, then we + // can seek the wav data too. + isSeekable = input->isSeekable && input->hasPosition; + + // Read header + unsigned int val; + + input->read(&val,4); // header + if(val != 0x46464952) // "RIFF" + fail("Not a WAV file"); + + input->read(&val,4); // size (ignored) + input->read(&val,4); // file format + if(val != 0x45564157) // "WAVE" + fail("Not a valid WAV file"); + + input->read(&val,4); // "fmt " + input->read(&val,4); // chunk size (must be 16) + if(val != 16) + fail("Unsupported WAV format"); + + input->read(&val,2); + if(val != 1) + fail("Non-PCM (compressed) WAV files not supported"); + + // Sound data specification + channels = 0; + input->read(&channels,2); + input->read(&rate, 4); + + // Skip next 6 bytes + input->read(&val, 4); + input->read(&val, 2); + + // Bits per sample + bits = 0; + input->read(&bits,2); + + input->read(&val,4); // Data header + if(val != 0x61746164) // "data" + fail("Expected data block"); + + // Finally, read the data size + input->read(&total,4); + left = total; + + // Store the beginning of the data block for later + if(input->hasPosition) + dataOffset = input->tell(); +} + +WavSource::WavSource(const std::string &file) +{ open(StreamPtr(new FileStream(file))); } diff --git a/sound/sources/wav_source.hpp b/sound/sources/wav_source.hpp new file mode 100644 index 000000000..227f4da73 --- /dev/null +++ b/sound/sources/wav_source.hpp @@ -0,0 +1,49 @@ +#ifndef MANGLE_SOUND_WAV_SOURCE_H +#define MANGLE_SOUND_WAV_SOURCE_H + +#include "../source.hpp" +#include + +namespace Mangle { +namespace Sound { + +/// WAV file decoder. Has no external library dependencies. +class WavSource : public SampleSource +{ + // Sound info + uint32_t rate, channels, bits; + + // Total size (of output) and bytes left + uint32_t total, left; + + // Offset in input of the beginning of the data block + size_t dataOffset; + + Mangle::Stream::StreamPtr input; + + void open(Mangle::Stream::StreamPtr); + + public: + /// Decode the given sound file + WavSource(const std::string&); + + /// Decode from stream + WavSource(Mangle::Stream::StreamPtr s) + { open(s); } + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); + + void seek(size_t); + size_t tell() const { return total-left; } + size_t size() const { return total; } + bool eof() const { return left > 0; } +}; + +#include "loadertemplate.hpp" + +/// A factory that loads WavSources from file and stream +typedef SSL_Template WavLoader; + +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 365e4a05c..6fcac72da 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,12 +1,18 @@ GCC=g++ -I../ -Wall -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test wav_source_test openal_various_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere +wav_source_test: wav_source_test.cpp ../sources/wav_source.cpp + $(GCC) $^ -o $@ + +openal_various_test: openal_various_test.cpp ../sources/mpg123_source.cpp ../sources/wav_source.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ -lmpg123 ${L_OPENAL} + openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) diff --git a/sound/tests/openal_various_test.cpp b/sound/tests/openal_various_test.cpp new file mode 100644 index 000000000..9426a672e --- /dev/null +++ b/sound/tests/openal_various_test.cpp @@ -0,0 +1,51 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_various.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_Various_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main() +{ + play("cow.wav"); + play("cow.wav", true); + return 0; +} diff --git a/sound/tests/output/openal_various_test.out b/sound/tests/output/openal_various_test.out new file mode 100644 index 000000000..f25a55513 --- /dev/null +++ b/sound/tests/output/openal_various_test.out @@ -0,0 +1 @@ +Playing cow.wav diff --git a/sound/tests/output/wav_source_test.out b/sound/tests/output/wav_source_test.out new file mode 100644 index 000000000..b6fc8e6fc --- /dev/null +++ b/sound/tests/output/wav_source_test.out @@ -0,0 +1,12 @@ +Source size: 37502 +rate=11025 +channels=1 +bits=16 +Reading entire buffer into memory + +Reading cow.raw +Size: 37502 + +Comparing... + +Done diff --git a/sound/tests/wav_source_test.cpp b/sound/tests/wav_source_test.cpp new file mode 100644 index 000000000..749af1849 --- /dev/null +++ b/sound/tests/wav_source_test.cpp @@ -0,0 +1,48 @@ +#include + +#include "../sources/wav_source.hpp" +#include "../../stream/servers/file_stream.hpp" + +#include +#include + +using namespace std; +using namespace Mangle::Sound; +using namespace Mangle::Stream; + +int main() +{ + WavSource wav("cow.wav"); + + cout << "Source size: " << wav.size() << endl; + int rate, channels, bits; + wav.getInfo(&rate, &channels, &bits); + cout << "rate=" << rate << "\nchannels=" << channels + << "\nbits=" << bits << endl; + + cout << "Reading entire buffer into memory\n"; + void *buf = malloc(wav.size()); + wav.read(buf, wav.size()); + + cout << "\nReading cow.raw\n"; + FileStream tmp("cow.raw"); + cout << "Size: " << tmp.size() << endl; + void *buf2 = malloc(tmp.size()); + tmp.read(buf2, tmp.size()); + + cout << "\nComparing...\n"; + if(tmp.size() != wav.size()) + { + cout << "SIZE MISMATCH!\n"; + assert(0); + } + + if(memcmp(buf, buf2, wav.size()) != 0) + { + cout << "CONTENT MISMATCH!\n"; + assert(0); + } + + cout << "\nDone\n"; + return 0; +} From 200fab03efaa2cbe1b8fce4a742b0195f8912295 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 18 Aug 2010 12:59:21 +0200 Subject: [PATCH 091/111] Improved WAV error checking --- sound/filters/openal_various.hpp | 5 ++--- sound/sources/mpg123_source.cpp | 5 ----- sound/sources/wav_source.cpp | 5 ++++- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/sound/filters/openal_various.hpp b/sound/filters/openal_various.hpp index 87030f4b6..945b3dabd 100644 --- a/sound/filters/openal_various.hpp +++ b/sound/filters/openal_various.hpp @@ -17,9 +17,8 @@ namespace Sound { MP3: mpg123 WAV: custom wav loader (PCM only) - This could be an alternative to using eg. libsndfile or other 3rd - party decoder libraries. (We implemented this for OpenMW because - we were experiencing crashes when using libsndfile.) + This could be an alternative to using eg. 3rd party decoder + libraries like libsndfile. */ class OpenAL_Various_Factory : public InputFilter { diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index 8a0dbd102..b0eeb77bb 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -106,11 +106,6 @@ Mpg123Source::Mpg123Source(const std::string &file) // This is the only bit size we support. bits = 16; - - // Ensure the output format does not change. (The tutorial on the - // mpg123 site did this, I assume it's kosher.) - mpg123_format_none(mhh); - mpg123_format(mhh,rate,channels,encoding); } Mpg123Source::~Mpg123Source() diff --git a/sound/sources/wav_source.cpp b/sound/sources/wav_source.cpp index 5ee87d492..8e3e8558c 100644 --- a/sound/sources/wav_source.cpp +++ b/sound/sources/wav_source.cpp @@ -30,7 +30,10 @@ size_t WavSource::read(void *data, size_t length) { if(length > left) length = left; - input->read(data, length); + size_t read = input->read(data, length); + if(read < length) + // Something went wrong + fail("WAV read error"); return length; } From 3324f6494c021e3dc69cd76ace5ff25a52e4bcce Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 19 Aug 2010 15:42:00 +0200 Subject: [PATCH 092/111] Added weak_ptr typedef to Sound (WSoundPtr), since this is commonly used --- sound/output.hpp | 1 + tools/shared_ptr.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/output.hpp b/sound/output.hpp index a9012b958..9bbdebb2c 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -26,6 +26,7 @@ namespace Sound { */ class Sound; typedef boost::shared_ptr SoundPtr; +typedef boost::weak_ptr WSoundPtr; class Sound { diff --git a/tools/shared_ptr.hpp b/tools/shared_ptr.hpp index da0b399bd..3d073fc24 100644 --- a/tools/shared_ptr.hpp +++ b/tools/shared_ptr.hpp @@ -1,3 +1,3 @@ // This file should include whatever it needs to define the boost/tr1 -// shared_ptr<> template. +// shared_ptr<> and weak_ptr<> templates. #include From 4a31f4e0f874b6e0715b7c195a44f5a3975fdb7e Mon Sep 17 00:00:00 2001 From: athile Date: Mon, 30 Aug 2010 03:09:44 +0100 Subject: [PATCH 094/111] Windows fixes --- sound/source.hpp | 4 ++-- stream/clients/audiere_file.hpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/source.hpp b/sound/source.hpp index 1eb42a283..8d2a801bc 100644 --- a/sound/source.hpp +++ b/sound/source.hpp @@ -28,8 +28,8 @@ class SampleSource : public Stream::Stream // Disabled functions by default. You can still override them in // subclasses. void seek(size_t pos) { assert(0); } - size_t tell() const { assert(0); } - size_t size() const { assert(0); } + size_t tell() const { assert(0); return 0; } + size_t size() const { assert(0); return 0; } }; typedef boost::shared_ptr SampleSourcePtr; diff --git a/stream/clients/audiere_file.hpp b/stream/clients/audiere_file.hpp index 691525ad9..61e26f21b 100644 --- a/stream/clients/audiere_file.hpp +++ b/stream/clients/audiere_file.hpp @@ -23,14 +23,14 @@ class AudiereFile : public audiere::RefImplementation : inp(_inp) {} /// Read 'count' bytes, return bytes successfully read - int read(void *buf, int count) + int ADR_CALL read(void *buf, int count) { return inp->read(buf,count); } /// Seek, relative to specified seek mode. Returns true if successful. - bool seek(int pos, audiere::File::SeekMode mode); + bool ADR_CALL seek(int pos, audiere::File::SeekMode mode); /// Get current position - int tell() + int ADR_CALL tell() { assert(inp->hasPosition); return inp->tell(); } }; From 2407f21c47abe218b9347df0fc9e8e9aebc02e3f Mon Sep 17 00:00:00 2001 From: athile Date: Mon, 30 Aug 2010 10:34:32 +0100 Subject: [PATCH 095/111] WIP Windows build --- sound/outputs/openal_out.cpp | 4 ++-- sound/outputs/openal_out.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 166b104e8..666dbe255 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -4,8 +4,8 @@ #include "../../stream/filters/buffer_stream.hpp" #include "../../tools/str_exception.hpp" -#include -#include +#include "al.h" +#include "alc.h" using namespace Mangle::Sound; diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index 1460e4ccc..44d03ecf8 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -30,8 +30,8 @@ class OpenAL_Factory : public SoundFactory OpenAL_Factory(bool doSetup = true); ~OpenAL_Factory(); - SoundPtr load(const std::string &file) { assert(0); } - SoundPtr load(Stream::StreamPtr input) { assert(0); } + SoundPtr load(const std::string &file) { assert(0); return SoundPtr(); } + SoundPtr load(Stream::StreamPtr input) { assert(0); return SoundPtr(); } SoundPtr loadRaw(SampleSourcePtr input); void update(); From 053074babb1445993ec40f72f60755de8d8ee5b7 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 2 Sep 2010 22:19:44 +0200 Subject: [PATCH 096/111] Killed str_error, std::runtime_error does the job --- rend2d/servers/sdl_driver.cpp | 10 +++++----- sound/filters/source_splicer.hpp | 4 ++-- sound/outputs/openal_out.cpp | 4 ++-- sound/sources/audiere_source.cpp | 5 +++-- sound/sources/ffmpeg_source.cpp | 4 ++-- sound/sources/libsndfile.cpp | 4 ++-- sound/sources/mpg123_source.cpp | 4 ++-- sound/sources/wav_source.cpp | 5 +++-- stream/servers/file_stream.hpp | 3 ++- stream/servers/outfile_stream.hpp | 2 +- stream/servers/std_ostream.hpp | 4 ++-- stream/servers/std_stream.hpp | 4 ++-- tools/str_exception.hpp | 25 ------------------------- 13 files changed, 28 insertions(+), 50 deletions(-) delete mode 100644 tools/str_exception.hpp diff --git a/rend2d/servers/sdl_driver.cpp b/rend2d/servers/sdl_driver.cpp index cc102e661..89c1c79c2 100644 --- a/rend2d/servers/sdl_driver.cpp +++ b/rend2d/servers/sdl_driver.cpp @@ -2,7 +2,7 @@ #include #include -#include "../../tools/str_exception.hpp" +#include #include using namespace Mangle::Rend2D; @@ -70,7 +70,7 @@ int SDL_Sprite::height() { return surface->h; } SDLDriver::SDLDriver() : display(NULL), realDisp(NULL), softDouble(false) { if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) - throw str_exception("Error initializing SDL video"); + throw std::runtime_error("Error initializing SDL video"); } SDLDriver::~SDLDriver() { @@ -94,7 +94,7 @@ void SDLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) // Create the surface and check it realDisp = SDL_SetVideoMode(width, height, bpp, flags); if(realDisp == NULL) - throw str_exception("Failed setting SDL video mode"); + throw std::runtime_error("Failed setting SDL video mode"); // Code for software double buffering. I haven't found this to be // any speed advantage at all in windowed mode (it's slower, as one @@ -160,7 +160,7 @@ Sprite* SDLDriver::loadImage(const std::string &file) SDL_Surface *surf = IMG_Load(file.c_str()); surf = convertImage(surf); if(surf == NULL) - throw str_exception("SDL failed to load image file '" + file + "'"); + throw std::runtime_error("SDL failed to load image file '" + file + "'"); return spriteFromSDL(surf); } @@ -171,7 +171,7 @@ Sprite* SDLDriver::loadImage(SDL_RWops *src, bool autoFree) SDL_Surface *surf = IMG_Load_RW(src, autoFree); surf = convertImage(surf); if(surf == NULL) - throw str_exception("SDL failed to load image"); + throw std::runtime_error("SDL failed to load image"); return spriteFromSDL(surf); } diff --git a/sound/filters/source_splicer.hpp b/sound/filters/source_splicer.hpp index 9fd49d126..9c7623086 100644 --- a/sound/filters/source_splicer.hpp +++ b/sound/filters/source_splicer.hpp @@ -2,7 +2,7 @@ #define MANGLE_SOUND_SOURCE_SPLICE_H #include "../source.hpp" -#include "../../tools/str_exception.hpp" +#include #include #include #include @@ -79,7 +79,7 @@ namespace Mangle if(catchAll) return catchAll->load(file); - throw str_exception("No handler for sound file " + file); + throw std::runtime_error("No handler for sound file " + file); } SampleSourcePtr load(Stream::StreamPtr input) { assert(0); } diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 666dbe255..dd2da29b3 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,8 +1,8 @@ #include "openal_out.hpp" #include +#include #include "../../stream/filters/buffer_stream.hpp" -#include "../../tools/str_exception.hpp" #include "al.h" #include "alc.h" @@ -26,7 +26,7 @@ static char tmp_buffer[BSIZE]; const int STREAM_BUF_NUM = 4; static void fail(const std::string &msg) -{ throw str_exception("OpenAL exception: " + msg); } +{ throw std::runtime_error("OpenAL exception: " + msg); } /* Check for AL error. Since we're always calling this with string diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 1f11272b0..9b97165be 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -1,12 +1,13 @@ #include "audiere_source.hpp" #include "../../stream/clients/audiere_file.hpp" -#include "../../tools/str_exception.hpp" + +#include using namespace Mangle::Stream; static void fail(const std::string &msg) -{ throw str_exception("Audiere exception: " + msg); } +{ throw std::runtime_error("Audiere exception: " + msg); } using namespace audiere; using namespace Mangle::Sound; diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index 2633515d8..fdc2ff954 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,6 +1,6 @@ #include "ffmpeg_source.hpp" -#include "../../tools/str_exception.hpp" +#include using namespace Mangle::Sound; @@ -9,7 +9,7 @@ using namespace Mangle::Sound; static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; static void fail(const std::string &msg) -{ throw str_exception("FFMpeg exception: " + msg); } +{ throw std::runtime_error("FFMpeg exception: " + msg); } // --- Loader --- diff --git a/sound/sources/libsndfile.cpp b/sound/sources/libsndfile.cpp index 9ac7ee465..b69a2d436 100644 --- a/sound/sources/libsndfile.cpp +++ b/sound/sources/libsndfile.cpp @@ -1,12 +1,12 @@ #include "libsndfile.hpp" -#include "../../tools/str_exception.hpp" +#include #include using namespace Mangle::Stream; static void fail(const std::string &msg) -{ throw str_exception("Mangle::libsndfile: " + msg); } +{ throw std::runtime_error("Mangle::libsndfile: " + msg); } using namespace Mangle::Sound; diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index b0eeb77bb..24d6ecce1 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -1,6 +1,6 @@ #include "mpg123_source.hpp" -#include "../../tools/str_exception.hpp" +#include #include @@ -28,7 +28,7 @@ using namespace Mangle::Stream; */ static void fail(const std::string &msg) -{ throw str_exception("Mangle::Mpg123 exception: " + msg); } +{ throw std::runtime_error("Mangle::Mpg123 exception: " + msg); } static void checkError(int err, void *mh = NULL) { diff --git a/sound/sources/wav_source.cpp b/sound/sources/wav_source.cpp index 8e3e8558c..a46b3d27e 100644 --- a/sound/sources/wav_source.cpp +++ b/sound/sources/wav_source.cpp @@ -1,13 +1,14 @@ #include "wav_source.hpp" -#include "../../tools/str_exception.hpp" #include "../../stream/servers/file_stream.hpp" +#include + using namespace Mangle::Stream; using namespace Mangle::Sound; static void fail(const std::string &msg) -{ throw str_exception("Mangle::Wav exception: " + msg); } +{ throw std::runtime_error("Mangle::Wav exception: " + msg); } void WavSource::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits) { diff --git a/stream/servers/file_stream.hpp b/stream/servers/file_stream.hpp index c789d2022..314a49642 100644 --- a/stream/servers/file_stream.hpp +++ b/stream/servers/file_stream.hpp @@ -3,6 +3,7 @@ #include "std_stream.hpp" #include +#include namespace Mangle { namespace Stream { @@ -20,7 +21,7 @@ class FileStream : public StdStream file.open(name.c_str(), std::ios::binary); if(file.fail()) - throw str_exception("FileStream: failed to open file " + name); + throw std::runtime_error("FileStream: failed to open file " + name); } ~FileStream() { file.close(); } }; diff --git a/stream/servers/outfile_stream.hpp b/stream/servers/outfile_stream.hpp index 2946ff853..8d953d904 100644 --- a/stream/servers/outfile_stream.hpp +++ b/stream/servers/outfile_stream.hpp @@ -30,7 +30,7 @@ class OutFileStream : public StdOStream file.open(name.c_str(), mode); if(file.fail()) - throw str_exception("OutFileStream: failed to open file " + name); + throw std::runtime_error("OutFileStream: failed to open file " + name); } ~OutFileStream() { file.close(); } }; diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp index f655477ff..797622631 100644 --- a/stream/servers/std_ostream.hpp +++ b/stream/servers/std_ostream.hpp @@ -3,7 +3,7 @@ #include "../stream.hpp" #include -#include "../../tools/str_exception.hpp" +#include namespace Mangle { namespace Stream { @@ -15,7 +15,7 @@ class StdOStream : public Stream std::ostream *inf; static void fail(const std::string &msg) - { throw str_exception("StdOStream: " + msg); } + { throw std::runtime_error("StdOStream: " + msg); } public: StdOStream(std::ostream *_inf) diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index 971c95415..68aca621b 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -3,7 +3,7 @@ #include "../stream.hpp" #include -#include "../../tools/str_exception.hpp" +#include namespace Mangle { namespace Stream { @@ -15,7 +15,7 @@ class StdStream : public Stream std::istream *inf; static void fail(const std::string &msg) - { throw str_exception("StdStream: " + msg); } + { throw std::runtime_error("StdStream: " + msg); } public: StdStream(std::istream *_inf) diff --git a/tools/str_exception.hpp b/tools/str_exception.hpp deleted file mode 100644 index 269501055..000000000 --- a/tools/str_exception.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __STR_EXCEPTION_H -#define __STR_EXCEPTION_H - -#include -#include - -/** @brief A simple exception that takes and holds a string - - Usage: - - throw str_exception("message"); - - */ -class str_exception : public std::exception -{ - std::string msg; - - public: - - str_exception(const std::string &m) : msg(m) {} - ~str_exception() throw() {} - const char* what() const throw() { return msg.c_str(); } -}; - -#endif From 57c5b3b75ddf2743d226f555a2883fd3b6cf599b Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 7 Sep 2010 11:08:09 +0200 Subject: [PATCH 097/111] Created std::istream/ostream wrapper + test. --- stream/clients/io_stream.hpp | 177 ++++++++++++++++++++++++++ stream/servers/std_ostream.hpp | 6 +- stream/stream.hpp | 38 ++++-- stream/tests/Makefile | 7 +- stream/tests/iostream_test.cpp | 132 +++++++++++++++++++ stream/tests/output/iostream_test.out | 21 +++ 6 files changed, 361 insertions(+), 20 deletions(-) create mode 100644 stream/clients/io_stream.hpp create mode 100644 stream/tests/iostream_test.cpp create mode 100644 stream/tests/output/iostream_test.out diff --git a/stream/clients/io_stream.hpp b/stream/clients/io_stream.hpp new file mode 100644 index 000000000..150f84cae --- /dev/null +++ b/stream/clients/io_stream.hpp @@ -0,0 +1,177 @@ +#ifndef MANGLE_STREAM_IOSTREAM_H +#define MANGLE_STREAM_IOSTREAM_H + +#include +#include "../stream.hpp" +#include +#include + +// This seems to work (TODO: test) +#ifndef EOF +#define EOF -1 +#endif + +namespace Mangle { +namespace Stream { + + /** This file contains classes for wrapping an std::istream or + std::ostream around a Mangle::Stream. Not to be confused with + servers/std_(o)stream.hpp, which do the opposite (wrap a + Mangle::Stream around an std::istream/ostream.) + + This allows you to use Mangle streams in places that require std + streams. The std::iostream interface is horrible and NOT + designed for easy subclassing. Defining your custom streams as + Mangle streams and then wrapping them here will usually be much + easier. + */ + class IOStreamBuffer : public std::streambuf + { + StreamPtr client; + std::vector ibuf, obuf; + + public: + IOStreamBuffer(StreamPtr strm) : client(strm) + { + // Set up input buffer + setg(NULL,NULL,NULL); + if(client->isReadable) + { + if(client->hasPtr) + { + assert(client->hasSize); + + // If the client supports direct pointer reading, then + // this is really easy. No internal buffer is needed. + char *ptr = (char*) client->getPtr(); + + // Set up the entire file as the input buffer + setg(ptr,ptr,ptr+client->size()); + } + else + { + // We need to do some manual slave labor. Set up an + // empty input buffer and let underflow() handle it. + ibuf.resize(1024); + } + } + + // Only create output buffer if the stream is writable + if(client->isWritable) + { + obuf.resize(1024); + /* Set beginning and end pointers, tells streambuf to write + to this area and call overflow() when it's full. + + Several examples use size-1, but the documentation for + streambuf clearly states that the end pointers is just + _past_ the last accessible position. + */ + char *beg = &obuf[0]; + setp(beg, beg+obuf.size()); + } + else + // Writing not permitted + setp(NULL, NULL); + } + + /* Underflow is called when there is no more info to read in the + input buffer. We need to refill ibuf with data (if any), and + set up the internal pointers with setg() to reflect the new + state. + */ + int underflow() + { + assert(client->isReadable); + + // If we've exhausted a pointer stream, then there's no more to + // be had. + if(client->hasPtr) + return EOF; + + // Read some more data + assert(ibuf.size()); + char *iptr = &ibuf[0]; + size_t read = client->read(iptr, ibuf.size()); + + // If we're out of data, then EOF + if(read == 0) + return EOF; + + // Otherwise, set up input buffer + setg(iptr, iptr, iptr+read); + + // Return the first char + return *((unsigned char*)iptr); + } + + /* Sync means to flush (write) all current data to the output + stream. It will also set up the entire output buffer to be + usable again. + */ + int sync() + { + assert(client->isWritable); + assert(obuf.size() > 0); + + // Get the number of bytes that streambuf wants us to write + int num = pptr() - pbase(); + assert(num >= 0); + + // Nothing to do + if(num == 0) return 0; + + if((int)client->write(pbase(), num) != num) + return -1; + + // Reset output buffer pointers + char *beg = &obuf[0]; + setp(beg, beg+obuf.size()); + + return 0; + } + + int overflow(int c=EOF) + { + // First, write all existing data + if(sync()) return EOF; + + // Put the requested character in the output + if(c != EOF) + { + *pptr() = c; + pbump(1); + } + + return 0; + } + }; + + class MangleIStream : public std::istream + { + IOStreamBuffer buf; + public: + MangleIStream(StreamPtr inp) + : std::istream(&buf) + , buf(inp) + { + assert(inp->isReadable); + } + }; + + class MangleOStream : public std::ostream + { + IOStreamBuffer buf; + public: + MangleOStream(StreamPtr inp) + : std::ostream(&buf) + , buf(inp) + { + assert(inp->isWritable); + } + + ~MangleOStream() { flush(); } + }; + +}} // namespaces +#endif diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp index 797622631..f406e1a93 100644 --- a/stream/servers/std_ostream.hpp +++ b/stream/servers/std_ostream.hpp @@ -25,11 +25,7 @@ class StdOStream : public Stream hasPosition = true; hasSize = true; isWritable = true; - } - - size_t read(void*,size_t) - { - assert(0&&"reading not supported by StdOStream"); + isReadable = false; } size_t write(const void* buf, size_t len) diff --git a/stream/stream.hpp b/stream/stream.hpp index aa9c14a9e..2ee4fcbd8 100644 --- a/stream/stream.hpp +++ b/stream/stream.hpp @@ -24,28 +24,38 @@ class Stream bool hasSize; /// If true, write() works. Writing through pointer operations is - /// not supported. + /// not (yet) supported. bool isWritable; + /// If true, read() and eof() works. + bool isReadable; + /// If true, the getPtr() functions work bool hasPtr; - /// Initialize all bools to false by default + /// Initialize all bools to false by default, except isReadable. Stream() : isSeekable(false), hasPosition(false), hasSize(false), - isWritable(false), hasPtr(false) {} + isWritable(false), isReadable(true), hasPtr(false) {} /// Virtual destructor virtual ~Stream() {} /** Read a given number of bytes from the stream. Returns the actual number read. If the return value is less than count, then the - stream is empty or an error occured. + stream is empty or an error occured. Only required for readable + streams. */ - virtual size_t read(void* buf, size_t count) = 0; + virtual size_t read(void* buf, size_t count) { assert(0); return 0; } /** Write a given number of bytes from the stream. Semantics is - similar to read(). Only valid if isWritable is true + similar to read(). Only valid if isWritable is true. + + The returned value is the number of bytes written. However in + most cases, unlike for read(), a write-count less than requested + usually indicates an error. The implementation should throw such + errors as exceptions rather than expect the caller to handle + them. Since most implementations do NOT support writing we default to an assert(0) here. @@ -57,18 +67,20 @@ class Stream /// Seek to an absolute position in this stream. Not all streams are /// seekable. - virtual void seek(size_t pos) = 0; + virtual void seek(size_t pos) { assert(0); } /// Get the current position in the stream. Non-seekable streams are /// not required to keep track of this. - virtual size_t tell() const = 0; + virtual size_t tell() const { assert(0); return 0; } - /// Return the total size of the stream. For streams where this is - /// not applicable, size() should return zero. - virtual size_t size() const = 0; + /// Return the total size of the stream. For streams hasSize is + /// false, size() should fail in some way, since it is an error to + /// call it in those cases. + virtual size_t size() const { assert(0); return 0; } - /// Returns true if the stream is empty - virtual bool eof() const = 0; + /// Returns true if the stream is empty. Required for readable + /// streams. + virtual bool eof() const { assert(0); return 0; } /// Return a pointer to the entire stream. This function (and the /// other getPtr() variants below) should only be implemented for diff --git a/stream/tests/Makefile b/stream/tests/Makefile index e64b90076..e4e32d115 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,6 +1,6 @@ -GCC=g++ -I../ -Wall +GCC=g++ -I../ -Wall -Werror -all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test +all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test iostream_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -12,6 +12,9 @@ ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream. audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) +iostream_test: iostream_test.cpp ../clients/io_stream.hpp + $(GCC) $< -o $@ + file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ diff --git a/stream/tests/iostream_test.cpp b/stream/tests/iostream_test.cpp new file mode 100644 index 000000000..7304e82fc --- /dev/null +++ b/stream/tests/iostream_test.cpp @@ -0,0 +1,132 @@ +#include +#include "../clients/io_stream.hpp" +#include "../servers/memory_stream.hpp" + +using namespace Mangle::Stream; +using namespace std; + +void test1() +{ + cout << "Testing ASCII reading from memory:\n"; + StreamPtr input(new MemoryStream("hello you world you", 19)); + MangleIStream inp(input); + + string str; + while(!inp.eof()) + { + inp >> str; + cout << "Got: " << str << endl; + } +} + +class Dummy : public Stream +{ + int count; + +public: + + Dummy() : count(0) + { + } + + size_t read(void *ptr, size_t num) + { + char *p = (char*)ptr; + char *start = p; + for(; (count < 2560) && (p-start < (int)num); count++) + { + *p = count / 10; + p++; + } + return p-start; + } + + bool eof() const { return count == 2560; } +}; + +void test2() +{ + cout << "Testing binary reading from non-memory:\n"; + + StreamPtr input(new Dummy); + MangleIStream inp(input); + + int x = 0; + while(!inp.eof()) + { + unsigned char buf[5]; + inp.read((char*)buf,5); + + // istream doesn't set eof() until we read _beyond_ the end of + // the stream, so we need an extra check. + if(inp.gcount() == 0) break; + + /* + for(int i=0;i<5;i++) + cout << (int)buf[i] << " "; + cout << endl; + */ + + assert(buf[4] == buf[0]); + assert(buf[0] == x/2); + x++; + } + cout << " Done\n"; +} + +struct Dummy2 : Stream +{ + Dummy2() + { + isWritable = true; + isReadable = false; + } + + size_t write(const void *ptr, size_t num) + { + const char *p = (const char*)ptr; + cout << " Got: "; + for(unsigned i=0;iwrite("testing", 7); + + cout << " Running through MangleOStream:\n"; + MangleOStream out(output); + out << "hello"; + out << " - are you ok?"; + cout << " Flushing:\n"; + out.flush(); + + cout << " Writing a hell of a lot of characters:\n"; + for(int i=0; i<127; i++) + out << "xxxxxxxx"; // 127 * 8 = 1016 + out << "fffffff"; // +7 = 1023 + cout << " Just one more:\n"; + out << "y"; + cout << " And oooone more:\n"; + out << "z"; + + cout << " Flushing again:\n"; + out.flush(); + cout << " Writing some more and exiting:\n"; + out << "blah bleh blob"; +} + +int main() +{ + test1(); + test2(); + test3(); + return 0; +} diff --git a/stream/tests/output/iostream_test.out b/stream/tests/output/iostream_test.out new file mode 100644 index 000000000..9c28cade3 --- /dev/null +++ b/stream/tests/output/iostream_test.out @@ -0,0 +1,21 @@ +Testing ASCII reading from memory: +Got: hello +Got: you +Got: world +Got: you +Testing binary reading from non-memory: + Done +Writing to dummy stream: + Pure dummy test: + Got: t e s t i n g + Running through MangleOStream: + Flushing: + Got: h e l l o - a r e y o u o k ? + Writing a hell of a lot of characters: + Just one more: + And oooone more: + Got: x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x f f f f f f f y + Flushing again: + Got: z + Writing some more and exiting: + Got: b l a h b l e h b l o b From d41b3c017d6cb864b7299be7c129c654c064e074 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 7 Sep 2010 13:12:21 +0200 Subject: [PATCH 098/111] Completed and refactored io_stream, split into hpp/cpp. --- stream/clients/io_stream.cpp | 221 ++++++++++++++++++++++++++ stream/clients/io_stream.hpp | 166 ++----------------- stream/tests/Makefile | 4 +- stream/tests/iostream_test.cpp | 48 +++++- stream/tests/output/iostream_test.out | 11 ++ 5 files changed, 296 insertions(+), 154 deletions(-) create mode 100644 stream/clients/io_stream.cpp diff --git a/stream/clients/io_stream.cpp b/stream/clients/io_stream.cpp new file mode 100644 index 000000000..5f1edc221 --- /dev/null +++ b/stream/clients/io_stream.cpp @@ -0,0 +1,221 @@ +#include "io_stream.hpp" + +// This seems to work +#ifndef EOF +#define EOF -1 +#endif + +using namespace Mangle::Stream; + +#define BSIZE 1024 + +// Streambuf for normal stream reading +class _istreambuf : public std::streambuf +{ + StreamPtr client; + char buf[BSIZE]; + +public: + _istreambuf(StreamPtr strm) : client(strm) + { + // Make sure we picked the right class + assert(client->isReadable); + assert(!client->hasPtr); + + // Tell streambuf to delegate reading operations to underflow() + setg(NULL,NULL,NULL); + + // Disallow writing + setp(NULL,NULL); + } + + /* Underflow is called when there is no more info to read in the + input buffer. We need to refill buf with new data (if any), and + set up the internal pointers with setg() to reflect the new + state. + */ + int underflow() + { + // Read some more data + size_t read = client->read(buf, BSIZE); + assert(read <= BSIZE); + + // If we're out of data, then EOF + if(read == 0) + return EOF; + + // Otherwise, set up input buffer + setg(buf, buf, buf+read); + + // Return the first char + return *((unsigned char*)buf); + } + + // Seek stream, if the source supports it. Ignores the second + // parameter as Mangle doesn't separate input and output pointers. + std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in) + { + // Does this stream know how to seek? + if(!client->isSeekable || !client->hasPosition) + // If not, signal an error. + return -1; + + // Set stream position and reset the buffer. + client->seek(pos); + setg(NULL,NULL,NULL); + + return client->tell(); + } +}; + +// Streambuf optimized for pointer-based input streams +class _ptrstreambuf : public std::streambuf +{ + StreamPtr client; + +public: + _ptrstreambuf(StreamPtr strm) : client(strm) + { + // Make sure we picked the right class + assert(client->isReadable); + assert(client->hasPtr); + + // seekpos() does all the work + seekpos(0); + } + + // Underflow is only called when we're at the end of the file + int underflow() { return EOF; } + + // Seek to a new position within the memory stream. This bypasses + // client->seek() entirely so isSeekable doesn't have to be set. + std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in) + { + // All pointer streams need a size + assert(client->hasSize); + + // Figure out how much will be left of the stream after seeking + size_t size = client->size() - pos; + + // Get a pointer + char* ptr = (char*)client->getPtr(pos,size); + + // And use it + setg(ptr,ptr,ptr+size); + + return pos; + } +}; + +// Streambuf for stream writing +class _ostreambuf : public std::streambuf +{ + StreamPtr client; + char buf[BSIZE]; + +public: + _ostreambuf(StreamPtr strm) : client(strm) + { + // Make sure we picked the right class + assert(client->isWritable); + + // Inform streambuf about our nice buffer + setp(buf, buf+BSIZE); + + // Disallow reading + setg(NULL,NULL,NULL); + } + + /* Sync means to flush (write) all current data to the output + stream. It will also set up the entire output buffer to be usable + again. + */ + int sync() + { + // Get the number of bytes that streambuf wants us to write + int num = pptr() - pbase(); + assert(num >= 0); + + // Is there any work to do? + if(num == 0) return 0; + + if((int)client->write(pbase(), num) != num) + // Inform caller that writing failed + return -1; + + // Reset output buffer pointers + setp(buf, buf+BSIZE); + + // No error + return 0; + } + + /* Called whenever the output buffer is full. + */ + int overflow(int c) + { + // First, write all existing data + if(sync()) return EOF; + + // Put the requested character in the next round of output + if(c != EOF) + { + *pptr() = c; + pbump(1); + } + + // No error + return 0; + } + + // Seek stream, if the source supports it. + std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::out) + { + if(!client->isSeekable || !client->hasPosition) + return -1; + + // Flush data and reset buffers + sync(); + + // Set stream position + client->seek(pos); + + return client->tell(); + } +}; + +MangleIStream::MangleIStream(StreamPtr inp) + : std::istream(NULL) +{ + assert(inp->isReadable); + + // Pick the right streambuf implementation based on whether the + // input supports pointers or not. + if(inp->hasPtr) + buf = new _ptrstreambuf(inp); + else + buf = new _istreambuf(inp); + + rdbuf(buf); +} + +MangleIStream::~MangleIStream() +{ + delete buf; +} + +MangleOStream::MangleOStream(StreamPtr out) + : std::ostream(NULL) +{ + assert(out->isWritable); + buf = new _ostreambuf(out); + + rdbuf(buf); +} + +MangleOStream::~MangleOStream() +{ + // Make sure we don't have lingering data on exit + flush(); + delete buf; +} diff --git a/stream/clients/io_stream.hpp b/stream/clients/io_stream.hpp index 150f84cae..98c6252ed 100644 --- a/stream/clients/io_stream.hpp +++ b/stream/clients/io_stream.hpp @@ -4,173 +4,39 @@ #include #include "../stream.hpp" #include -#include - -// This seems to work (TODO: test) -#ifndef EOF -#define EOF -1 -#endif namespace Mangle { namespace Stream { /** This file contains classes for wrapping an std::istream or - std::ostream around a Mangle::Stream. Not to be confused with - servers/std_(o)stream.hpp, which do the opposite (wrap a - Mangle::Stream around an std::istream/ostream.) + std::ostream around a Mangle::Stream. This allows you to use Mangle streams in places that require std - streams. The std::iostream interface is horrible and NOT - designed for easy subclassing. Defining your custom streams as - Mangle streams and then wrapping them here will usually be much - easier. - */ - class IOStreamBuffer : public std::streambuf - { - StreamPtr client; - std::vector ibuf, obuf; - - public: - IOStreamBuffer(StreamPtr strm) : client(strm) - { - // Set up input buffer - setg(NULL,NULL,NULL); - if(client->isReadable) - { - if(client->hasPtr) - { - assert(client->hasSize); - - // If the client supports direct pointer reading, then - // this is really easy. No internal buffer is needed. - char *ptr = (char*) client->getPtr(); - - // Set up the entire file as the input buffer - setg(ptr,ptr,ptr+client->size()); - } - else - { - // We need to do some manual slave labor. Set up an - // empty input buffer and let underflow() handle it. - ibuf.resize(1024); - } - } - - // Only create output buffer if the stream is writable - if(client->isWritable) - { - obuf.resize(1024); - /* Set beginning and end pointers, tells streambuf to write - to this area and call overflow() when it's full. - - Several examples use size-1, but the documentation for - streambuf clearly states that the end pointers is just - _past_ the last accessible position. - */ - char *beg = &obuf[0]; - setp(beg, beg+obuf.size()); - } - else - // Writing not permitted - setp(NULL, NULL); - } - - /* Underflow is called when there is no more info to read in the - input buffer. We need to refill ibuf with data (if any), and - set up the internal pointers with setg() to reflect the new - state. - */ - int underflow() - { - assert(client->isReadable); - - // If we've exhausted a pointer stream, then there's no more to - // be had. - if(client->hasPtr) - return EOF; - - // Read some more data - assert(ibuf.size()); - char *iptr = &ibuf[0]; - size_t read = client->read(iptr, ibuf.size()); - - // If we're out of data, then EOF - if(read == 0) - return EOF; + streams. - // Otherwise, set up input buffer - setg(iptr, iptr, iptr+read); - - // Return the first char - return *((unsigned char*)iptr); - } - - /* Sync means to flush (write) all current data to the output - stream. It will also set up the entire output buffer to be - usable again. - */ - int sync() - { - assert(client->isWritable); - assert(obuf.size() > 0); - - // Get the number of bytes that streambuf wants us to write - int num = pptr() - pbase(); - assert(num >= 0); - - // Nothing to do - if(num == 0) return 0; - - if((int)client->write(pbase(), num) != num) - return -1; - - // Reset output buffer pointers - char *beg = &obuf[0]; - setp(beg, beg+obuf.size()); - - return 0; - } - - int overflow(int c=EOF) - { - // First, write all existing data - if(sync()) return EOF; - - // Put the requested character in the output - if(c != EOF) - { - *pptr() = c; - pbump(1); - } - - return 0; - } - }; + This is much easier than trying to make your own custom streams + into iostreams. The std::iostream interface is horrible and NOT + designed for easy subclassing. Create a Mangle::Stream instead, + and use this wrapper. + */ + // An istream wrapping a readable Mangle::Stream. Has extra + // optimizations for pointer-based streams. class MangleIStream : public std::istream { - IOStreamBuffer buf; + std::streambuf *buf; public: - MangleIStream(StreamPtr inp) - : std::istream(&buf) - , buf(inp) - { - assert(inp->isReadable); - } + MangleIStream(StreamPtr inp); + ~MangleIStream(); }; + // An ostream wrapping a writable Mangle::Stream. class MangleOStream : public std::ostream { - IOStreamBuffer buf; + std::streambuf *buf; public: - MangleOStream(StreamPtr inp) - : std::ostream(&buf) - , buf(inp) - { - assert(inp->isWritable); - } - - ~MangleOStream() { flush(); } + MangleOStream(StreamPtr inp); + ~MangleOStream(); }; }} // namespaces diff --git a/stream/tests/Makefile b/stream/tests/Makefile index e4e32d115..5f4397819 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -12,8 +12,8 @@ ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream. audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -iostream_test: iostream_test.cpp ../clients/io_stream.hpp - $(GCC) $< -o $@ +iostream_test: iostream_test.cpp ../clients/io_stream.cpp + $(GCC) $^ -o $@ file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ diff --git a/stream/tests/iostream_test.cpp b/stream/tests/iostream_test.cpp index 7304e82fc..6acebbbd6 100644 --- a/stream/tests/iostream_test.cpp +++ b/stream/tests/iostream_test.cpp @@ -46,7 +46,7 @@ public: void test2() { - cout << "Testing binary reading from non-memory:\n"; + cout << "\nTesting binary reading from non-memory:\n"; StreamPtr input(new Dummy); MangleIStream inp(input); @@ -95,7 +95,7 @@ struct Dummy2 : Stream void test3() { - cout << "Writing to dummy stream:\n"; + cout << "\nWriting to dummy stream:\n"; cout << " Pure dummy test:\n"; StreamPtr output(new Dummy2); @@ -123,10 +123,54 @@ void test3() out << "blah bleh blob"; } +struct Dummy3 : Stream +{ + int pos; + + Dummy3() : pos(0) + { + hasPosition = true; + isSeekable = true; + } + + size_t read(void*, size_t num) + { + cout << " Reading " << num << " bytes from " << pos << endl; + pos += num; + return num; + } + + void seek(size_t npos) { pos = npos; } + size_t tell() const { return pos; } +}; + +void test4() +{ + cout << "\nTesting seeking;\n"; + StreamPtr input(new Dummy3); + + cout << " Direct reading:\n"; + input->read(0,10); + input->read(0,5); + + MangleIStream inp(input); + + cout << " Reading from istream:\n"; + char buf[20]; + inp.read(buf, 20); + inp.read(buf, 20); + inp.read(buf, 20); + + cout << " Seeking to 30 and reading again:\n"; + inp.seekg(30); + inp.read(buf, 20); +} + int main() { test1(); test2(); test3(); + test4(); return 0; } diff --git a/stream/tests/output/iostream_test.out b/stream/tests/output/iostream_test.out index 9c28cade3..b6da80c80 100644 --- a/stream/tests/output/iostream_test.out +++ b/stream/tests/output/iostream_test.out @@ -3,8 +3,10 @@ Got: hello Got: you Got: world Got: you + Testing binary reading from non-memory: Done + Writing to dummy stream: Pure dummy test: Got: t e s t i n g @@ -19,3 +21,12 @@ Writing to dummy stream: Got: z Writing some more and exiting: Got: b l a h b l e h b l o b + +Testing seeking; + Direct reading: + Reading 10 bytes from 0 + Reading 5 bytes from 10 + Reading from istream: + Reading 1024 bytes from 15 + Seeking to 30 and reading again: + Reading 1024 bytes from 30 From fb95756a50ca0b77e0588c4c249c26b00bcf6011 Mon Sep 17 00:00:00 2001 From: athile Date: Tue, 7 Sep 2010 15:17:46 +0100 Subject: [PATCH 099/111] Fix crash at shutdown --- sound/clients/ogre_listener_mover.hpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/clients/ogre_listener_mover.hpp b/sound/clients/ogre_listener_mover.hpp index d1cfcb65c..739c08a13 100644 --- a/sound/clients/ogre_listener_mover.hpp +++ b/sound/clients/ogre_listener_mover.hpp @@ -9,7 +9,7 @@ namespace Mangle { namespace Sound { /** This class lets a sound listener (ie. the SoundFactory) track a - given camera in Ogre3D. The poisition and orientation of the + given camera in Ogre3D. The position and orientation of the listener will be updated to match the camera whenever the camera is moved. */ @@ -27,6 +27,16 @@ namespace Sound { camera->addListener(this); } + void unfollowCamera() + { + // If the camera is null, this object wasn't following a camera. + // It doesn't make sense to call unfollow + assert(camera != NULL); + + camera->removeListener(this); + camera = NULL; + } + private: Mangle::Sound::SoundFactoryPtr soundFact; Ogre::Camera *camera; From 4d2573038867df3857501ad6bad8ef0d75807112 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 8 Sep 2010 15:09:39 +0200 Subject: [PATCH 100/111] Added SDL+OpenGL 2D rendering backend (ony basic features) --- rend2d/servers/sdl_gl_driver.cpp | 321 ++++++++++++++++++++++++ rend2d/servers/sdl_gl_driver.hpp | 132 ++++++++++ rend2d/tests/Makefile | 10 +- rend2d/tests/output/sdl_move_test.out | 0 rend2d/tests/output/sdlgl_move_test.out | 0 rend2d/tests/sdl_move_test.cpp | 30 +++ rend2d/tests/sdlgl_move_test.cpp | 31 +++ 7 files changed, 522 insertions(+), 2 deletions(-) create mode 100644 rend2d/servers/sdl_gl_driver.cpp create mode 100644 rend2d/servers/sdl_gl_driver.hpp create mode 100644 rend2d/tests/output/sdl_move_test.out create mode 100644 rend2d/tests/output/sdlgl_move_test.out create mode 100644 rend2d/tests/sdl_move_test.cpp create mode 100644 rend2d/tests/sdlgl_move_test.cpp diff --git a/rend2d/servers/sdl_gl_driver.cpp b/rend2d/servers/sdl_gl_driver.cpp new file mode 100644 index 000000000..aadb6abce --- /dev/null +++ b/rend2d/servers/sdl_gl_driver.cpp @@ -0,0 +1,321 @@ +#include "sdl_gl_driver.hpp" + +#include +#include +#include +#include +#include + +using namespace Mangle::Rend2D; + +void SDLGL_Sprite::draw(Sprite *s, // Must be SDLGL_Sprite + int x, int y, // Destination position + int sx, int sy, // Source position + int w, int h // Amount to draw. -1 means remainder. + ) +{ + // Get source surface + SDLGL_Sprite *other = dynamic_cast(s); + assert(other != NULL); + SDL_Surface *img = other->getSurface(); + + // Check coordinate validity + assert(sx <= img->w && sy <= img->h); + assert(x <= surface->w && y <= surface->h); + assert(sx >= 0 && sy >= 0); + + // Compute width and height if necessary + if(w == -1) w = img->w - sx; + if(h == -1) h = img->h - sy; + + // Check them if they're valid + assert(w >= 0 && w <= img->w); + assert(h >= 0 && h <= img->h); + + SDL_Rect dest; + dest.x = x; + dest.y = y; + dest.w = w; + dest.h = h; + + SDL_Rect src; + src.x = sx; + src.y = sy; + src.w = w; + src.h = h; + + // Do the Blitman + SDL_BlitSurface(img, &src, surface, &dest); +} + +SDLGL_Sprite::SDLGL_Sprite(SDL_Surface *s, bool autoDelete) + : surface(s), autoDel(autoDelete) +{ + assert(surface != NULL); +} + +SDLGL_Sprite::~SDLGL_Sprite() +{ + if(autoDel) + SDL_FreeSurface(surface); +} + +void SDLGL_Sprite::fill(int value) +{ + SDL_FillRect(surface, NULL, value); +} + +int SDLGL_Sprite::width() { return surface->w; } +int SDLGL_Sprite::height() { return surface->h; } + +SDLGLDriver::SDLGLDriver() : display(NULL), realDisp(NULL) +{ + if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) + throw std::runtime_error("Error initializing SDL video"); +} +SDLGLDriver::~SDLGLDriver() +{ + if(display) delete display; + SDL_Quit(); +} + +// Surface used for the screen. Since OpenGL surfaces must have sizes +// that are powers of 2, we have to "fake" the returned display size +// to match the screen, not the surface itself. If we don't use this, +// the client program will get confused about the actual size of our +// screen, thinking it is bigger than it is. +struct FakeSizeSprite : SDLGL_Sprite +{ + int fakeW, fakeH; + + FakeSizeSprite(SDL_Surface *s, int fw, int fh) + : SDLGL_Sprite(s), fakeW(fw), fakeH(fh) + {} + + int width() { return fakeW; } + int height() { return fakeH; } +}; + +static int makePow2(int num) +{ + assert(num); + if((num & (num-1)) != 0) + { + int cnt = 0; + while(num) + { + num >>= 1; + cnt++; + } + num = 1 << cnt; + } + return num; +} + +void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) +{ + unsigned int flags; + + if(display) delete display; + + flags = SDL_OPENGL; + + if (fullscreen) + flags |= SDL_FULLSCREEN; + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); + + // Create the surface and check it + screen = SDL_SetVideoMode(width, height, bpp, flags); + if(screen == NULL) + throw std::runtime_error("Failed setting SDL video mode"); + + // Expand width and height to be powers of 2 + int width2 = makePow2(width); + int height2 = makePow2(height); + + // Create a new SDL surface of this size + const SDL_PixelFormat& fmt = *(screen->format); + realDisp = SDL_CreateRGBSurface(SDL_SWSURFACE,width2,height2, + fmt.BitsPerPixel, + fmt.Rmask,fmt.Gmask,fmt.Bmask,fmt.Amask); + + // Create a sprite directly representing the display surface. This + // allows the user to blit to it directly. + display = new FakeSizeSprite(realDisp, width, height); + + // Set up the OpenGL format + nOfColors = fmt.BytesPerPixel; + + if(nOfColors == 4) + { + if (fmt.Rmask == 0x000000ff) + texture_format = GL_RGBA; + else + texture_format = GL_BGRA; + } + else if(nOfColors == 3) + { + if (fmt.Rmask == 0x000000ff) + texture_format = GL_RGB; + else + texture_format = GL_BGR; + } + else + assert(0 && "unsupported screen format"); + + glEnable(GL_TEXTURE_2D); + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); +} + +void SDLGLDriver::updateNoSwap() +{ + if(!realDisp) return; + + // Fist, set up the screen texture: + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Edit the texture object's image data + glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, realDisp->w, realDisp->h, 0, + texture_format, GL_UNSIGNED_BYTE, realDisp->pixels ); + + glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + // OpenGL barf. Set up the projection to match our screen + int vPort[4]; + glGetIntegerv(GL_VIEWPORT, vPort); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, vPort[2], 0, vPort[3], -1, 1); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glBegin( GL_QUADS ); + + // Bottom-left vertex (corner) + glTexCoord2i( 0, 0 ); + glVertex3f(0,0,0); + + // Bottom-right vertex (corner) + glTexCoord2i( 1, 0 ); + glVertex3f( realDisp->w, 0, 0.f ); + + // Top-right vertex (corner) + glTexCoord2i( 1, 1 ); + glVertex3f( realDisp->w, realDisp->h, 0.f ); + + // Top-left vertex (corner) + glTexCoord2i( 0, 1 ); + glVertex3f( 0, realDisp->h, 0.f ); + glEnd(); + + /* + glBegin(GL_TRIANGLES); + glColor3ub(255, 0, 0); + glVertex2d(0, 0); + + glColor3ub(0, 255, 0); + glVertex2d(100,0); + + glColor3ub(0, 0, 255); + glVertex2d(50, 50); + glEnd(); + */ + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +void SDLGLDriver::swap() +{ + SDL_GL_SwapBuffers(); +} + +void SDLGLDriver::update() +{ + updateNoSwap(); + swap(); +} + +/// Set the window title, as well as the title of the window when +/// "iconified" +void SDLGLDriver::setWindowTitle(const std::string &title, + const std::string &icon) +{ + SDL_WM_SetCaption( title.c_str(), icon.c_str() ); +} + +// Convert the given surface to display format. +static SDL_Surface* convertImage(SDL_Surface* surf) +{ + if(surf != NULL) + { + // Convert the image to the display buffer format, for faster + // blitting + SDL_Surface *surf2 = SDL_DisplayFormat(surf); + SDL_FreeSurface(surf); + surf = surf2; + } + return surf; +} + +/// Load sprite from an image file, using SDL_image. +Sprite* SDLGLDriver::loadImage(const std::string &file) +{ + SDL_Surface *surf = IMG_Load(file.c_str()); + surf = convertImage(surf); + if(surf == NULL) + throw std::runtime_error("SDL failed to load image file '" + file + "'"); + return spriteFromSDL(surf); +} + +/// Load sprite from an SDL_RWops structure. autoFree determines +/// whether the RWops struct should be closed/freed after use. +Sprite* SDLGLDriver::loadImage(SDL_RWops *src, bool autoFree) +{ + SDL_Surface *surf = IMG_Load_RW(src, autoFree); + surf = convertImage(surf); + if(surf == NULL) + throw std::runtime_error("SDL failed to load image"); + return spriteFromSDL(surf); +} + +/// Load a sprite from an image file stored in memory. Uses +/// SDL_image. +Sprite* SDLGLDriver::loadImage(const void* data, size_t size) +{ + SDL_RWops *rw = SDL_RWFromConstMem(data, size); + return loadImage(rw, true); +} + +void SDLGLDriver::setGamma(float red, float green, float blue) +{ + SDL_SetGamma(red,green,blue); +} + +/// Convert an existing SDL surface into a sprite +Sprite* SDLGLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) +{ + assert(surf); + return new SDLGL_Sprite(surf, autoFree); +} + +void SDLGLDriver::sleep(int ms) { SDL_Delay(ms); } +unsigned int SDLGLDriver::ticks() { return SDL_GetTicks(); } diff --git a/rend2d/servers/sdl_gl_driver.hpp b/rend2d/servers/sdl_gl_driver.hpp new file mode 100644 index 000000000..d116e3659 --- /dev/null +++ b/rend2d/servers/sdl_gl_driver.hpp @@ -0,0 +1,132 @@ +#ifndef MANGLE_DRAW2D_SDLGL_H +#define MANGLE_DRAW2D_SDLGL_H + +/** This driver is similar to SDLDriver, except that it uses SDL on + top of OpenGL. + + I've decided to make it a separate file instead of just adding + optional OpenGL support to the original, so that pure SDL users + don't have to add OpenGL as a dependency. + */ + +#include "../driver.hpp" + +// Predeclarations keep the streets safe at night +struct SDL_Surface; +struct SDL_RWops; + +namespace Mangle +{ + namespace Rend2D + { + /// SDL-implementation of Sprite + struct SDLGL_Sprite : Sprite + { + /** Draw a sprite in the given position. Can only draw other SDL + sprites. + */ + void draw(Sprite *s, // Must be SDLGL_Sprite + int x, int y, // Destination position + int sx=0, int sy=0, // Source position + int w=-1, int h=-1 // Amount to draw. -1 means remainder. + ); + + SDLGL_Sprite(SDL_Surface *s, bool autoDelete=true); + ~SDLGL_Sprite(); + + // Information retrieval + virtual int width(); + virtual int height(); + SDL_Surface *getSurface() { return surface; } + + // Fill with a given pixel value + void fill(int value); + + private: + // The SDL surface + SDL_Surface* surface; + + // If true, delete this surface when the canvas is destructed + bool autoDel; + }; + + class SDLGLDriver : public Driver + { + // The main display surface + SDLGL_Sprite *display; + + // The screen surface. This is completely unused. + SDL_Surface *screen; + + // The display surface and main GL texture. These are used when + // drawing the entire screen as one surface, as a drop-in + // replacement for SDLDriver. + SDL_Surface *realDisp; + unsigned int texture; + int nOfColors, texture_format; + + public: + SDLGLDriver(); + ~SDLGLDriver(); + + /// Sets the video mode. Will create the window if it is not + /// already set up. Note that for SDL, bpp=0 means use current + /// bpp. + void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); + + /// Update the screen + void update(); + + /// Calls SDL_GL_SwapBuffers + void swap(); + + /// Draw surface to screen but do not call SDL_GL_SwapBuffers() + void updateNoSwap(); + + /// Set the window title, as well as the title of the window + /// when "iconified" + void setWindowTitle(const std::string &title, + const std::string &icon); + + // Include overloads from our Glorious parent + using Driver::setWindowTitle; + + /// Load sprite from an image file, using SDL_image. + Sprite* loadImage(const std::string &file); + + /// Load sprite from an SDL_RWops structure. autoFree determines + /// whether the RWops struct should be closed/freed after use. + Sprite* loadImage(SDL_RWops *src, bool autoFree=false); + + /// Load a sprite from an image file stored in memory. Uses + /// SDL_image. + Sprite* loadImage(const void* data, size_t size); + + /// Set gamma value + void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } + + /// Set gamma individually for red, green, blue + void setGamma(float red, float green, float blue); + + /// Convert an existing SDL surface into a sprite + Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); + + // Get width and height + int width() { return display ? display->width() : 0; } + int height() { return display ? display->height() : 0; } + + /// Get the screen sprite + Sprite *getScreen() { return display; } + + /// Not really a graphic-related function, but very + /// handly. Sleeps the given number of milliseconds using + /// SDL_Delay(). + void sleep(int ms); + + /// Get the number of ticks since SDL initialization, using + /// SDL_GetTicks(). + unsigned int ticks(); + }; + } +} +#endif diff --git a/rend2d/tests/Makefile b/rend2d/tests/Makefile index efbbda5d6..d430f60a9 100644 --- a/rend2d/tests/Makefile +++ b/rend2d/tests/Makefile @@ -1,9 +1,15 @@ -GCC=g++ -Wall +GCC=g++ -Wall -Werror -all: sdl_test +all: sdl_test sdl_move_test sdlgl_move_test sdl_test: sdl_test.cpp $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image +sdl_move_test: sdl_move_test.cpp ../servers/sdl_driver.cpp + $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image + +sdlgl_move_test: sdlgl_move_test.cpp ../servers/sdl_gl_driver.cpp + $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image -lGL + clean: rm *_test diff --git a/rend2d/tests/output/sdl_move_test.out b/rend2d/tests/output/sdl_move_test.out new file mode 100644 index 000000000..e69de29bb diff --git a/rend2d/tests/output/sdlgl_move_test.out b/rend2d/tests/output/sdlgl_move_test.out new file mode 100644 index 000000000..e69de29bb diff --git a/rend2d/tests/sdl_move_test.cpp b/rend2d/tests/sdl_move_test.cpp new file mode 100644 index 000000000..bfbca98fa --- /dev/null +++ b/rend2d/tests/sdl_move_test.cpp @@ -0,0 +1,30 @@ +#include +#include + +using namespace std; + +#include "../servers/sdl_driver.hpp" + +using namespace Mangle::Rend2D; + +int main() +{ + SDLDriver sdl; + + sdl.setVideoMode(640,480,0,false); + sdl.setWindowTitle("Testing 123"); + Sprite *screen = sdl.getScreen(); + const char* imgName = "tile1-blue.png"; + Sprite *image = sdl.loadImage(imgName); + + for(int frames=0; frames<170; frames++) + { + screen->fill(0); + for(int j=0; j<10; j++) + for(int i=0; i<25; i++) + screen->draw(image, 2*frames+30*j, 20*i); + sdl.update(); + sdl.sleep(10); + } + return 0; +} diff --git a/rend2d/tests/sdlgl_move_test.cpp b/rend2d/tests/sdlgl_move_test.cpp new file mode 100644 index 000000000..b769ee837 --- /dev/null +++ b/rend2d/tests/sdlgl_move_test.cpp @@ -0,0 +1,31 @@ +#include +#include + +using namespace std; + +#include "../servers/sdl_gl_driver.hpp" + +using namespace Mangle::Rend2D; + +int main() +{ + SDLGLDriver sdl; + + sdl.setVideoMode(640,480,0,false); + sdl.setWindowTitle("Testing 123"); + Sprite *screen = sdl.getScreen(); + const char* imgName = "tile1-blue.png"; + Sprite *image = sdl.loadImage(imgName); + + for(int frames=0; frames<170; frames++) + { + screen->fill(0); + for(int j=0; j<10; j++) + for(int i=0; i<25; i++) + screen->draw(image, 2*frames+30*j, 20*i); + sdl.update(); + sdl.sleep(5); + } + + return 0; +} From 32966b80cbdc0fc132f38391b2c365344174b63a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 8 Sep 2010 15:19:04 +0200 Subject: [PATCH 101/111] Fixed screen transformation --- rend2d/servers/sdl_gl_driver.cpp | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/rend2d/servers/sdl_gl_driver.cpp b/rend2d/servers/sdl_gl_driver.cpp index aadb6abce..e2e829a7a 100644 --- a/rend2d/servers/sdl_gl_driver.cpp +++ b/rend2d/servers/sdl_gl_driver.cpp @@ -207,35 +207,25 @@ void SDLGLDriver::updateNoSwap() glBegin( GL_QUADS ); + // Needed to move the screen into the right place + int diff = screen->h - realDisp->h; + // Bottom-left vertex (corner) - glTexCoord2i( 0, 0 ); - glVertex3f(0,0,0); + glTexCoord2i( 0, 1 ); + glVertex3f(0,diff,0); // Bottom-right vertex (corner) - glTexCoord2i( 1, 0 ); - glVertex3f( realDisp->w, 0, 0.f ); + glTexCoord2i( 1, 1 ); + glVertex3f( realDisp->w, diff, 0.f ); // Top-right vertex (corner) - glTexCoord2i( 1, 1 ); - glVertex3f( realDisp->w, realDisp->h, 0.f ); + glTexCoord2i( 1, 0 ); + glVertex3f( realDisp->w, screen->h, 0.f ); // Top-left vertex (corner) - glTexCoord2i( 0, 1 ); - glVertex3f( 0, realDisp->h, 0.f ); - glEnd(); - - /* - glBegin(GL_TRIANGLES); - glColor3ub(255, 0, 0); - glVertex2d(0, 0); - - glColor3ub(0, 255, 0); - glVertex2d(100,0); - - glColor3ub(0, 0, 255); - glVertex2d(50, 50); + glTexCoord2i( 0, 0 ); + glVertex3f( 0, screen->h, 0.f ); glEnd(); - */ glMatrixMode(GL_PROJECTION); glPopMatrix(); From 32a2d1650ccaf3fb478862922908bdf8d2123c6a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 10 Sep 2010 10:57:30 +0200 Subject: [PATCH 102/111] Fixed OpenAL cloning bug --- sound/outputs/openal_out.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index dd2da29b3..4a3c875d7 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -364,7 +364,7 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact) { // Increase the reference count assert(ref != NULL); - *refCnt++; + (*refCnt)++; // Set up buffer bufferID[0] = buf; @@ -469,7 +469,7 @@ OpenAL_Sound::~OpenAL_Sound() owner->notifyDelete(this); // Decrease the reference counter - if((-- *refCnt) == 0) + if((-- (*refCnt)) == 0) { // We're the last owner. Delete the buffer(s) and the counter // itself. From 7f26ba2ba3aa27a2d6d740ab508b6687ea012827 Mon Sep 17 00:00:00 2001 From: Jan Borsodi Date: Thu, 9 Sep 2010 23:25:11 +0200 Subject: [PATCH 103/111] Fix for Visual Studio 2008. stdint.h is not bundled with VS 2008, instead we use cstdint.hpp from BOOST. --- sound/outputs/openal_out.cpp | 5 +++-- sound/source.hpp | 4 +++- sound/sources/audiere_source.cpp | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 4a3c875d7..c0cae640e 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -55,8 +55,9 @@ static void checkALError(const char *where) static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) { - int ch, bits; - inp->getInfo(&rate, &ch, &bits); + int32_t rate_, ch, bits; + inp->getInfo(&rate_, &ch, &bits); + rate = rate_; fmt = 0; diff --git a/sound/source.hpp b/sound/source.hpp index 8d2a801bc..fbe7cf958 100644 --- a/sound/source.hpp +++ b/sound/source.hpp @@ -2,7 +2,7 @@ #define MANGLE_SOUND_SOURCE_H #include -#include +#include #include #include "../stream/stream.hpp" @@ -10,6 +10,8 @@ namespace Mangle { namespace Sound { +typedef boost::int32_t int32_t; + /// A stream containing raw sound data and information about the format class SampleSource : public Stream::Stream { diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 9b97165be..4e5b6adce 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -17,7 +17,10 @@ using namespace Mangle::Sound; void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) { SampleFormat fmt; - sample->getFormat(*channels, *rate, fmt); + int channels_, rate_; + sample->getFormat(channels_, rate_, fmt); + *channels = channels_; + *rate = rate_; if(bits) { if(fmt == SF_U8) From 7a22068da611235190fa7343ca3f8c1d1944a68c Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 10 Sep 2010 13:57:35 +0200 Subject: [PATCH 104/111] Fixed conflict in last commit --- sound/outputs/openal_out.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index c0cae640e..d0ce47718 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -55,7 +55,7 @@ static void checkALError(const char *where) static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) { - int32_t rate_, ch, bits; + boost::int32_t rate_, ch, bits; inp->getInfo(&rate_, &ch, &bits); rate = rate_; From 7345f2307f3ce6682a4044b98a811fac2cb7c4f0 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 13 Sep 2010 10:52:26 +0200 Subject: [PATCH 105/111] Improved OpenAL includes. Hacked std_stream to make seek() able to recover from eof(). --- sound/outputs/openal_out.cpp | 12 ++++++++++-- stream/servers/std_stream.hpp | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index d0ce47718..bce08d02f 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -4,8 +4,16 @@ #include "../../stream/filters/buffer_stream.hpp" -#include "al.h" -#include "alc.h" +#ifdef _WIN32 +#include +#include +#elif defined(__APPLE__) +#include +#include +#else +#include +#include +#endif using namespace Mangle::Sound; diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index 68aca621b..163f023f6 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -36,6 +36,7 @@ class StdStream : public Stream void seek(size_t pos) { + inf->clear(); inf->seekg(pos); if(inf->fail()) fail("seek error"); From 593ac7b240a517d8412fc806cce9680f924ab5f0 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sun, 19 Sep 2010 11:57:09 +0200 Subject: [PATCH 106/111] Added pixel() to Rend2D::Sprite --- rend2d/servers/sdl_driver.cpp | 32 ++++++++++++++++++++++++++++++++ rend2d/servers/sdl_driver.hpp | 3 +++ rend2d/sprite.hpp | 5 +++++ 3 files changed, 40 insertions(+) diff --git a/rend2d/servers/sdl_driver.cpp b/rend2d/servers/sdl_driver.cpp index 89c1c79c2..d86cf53e4 100644 --- a/rend2d/servers/sdl_driver.cpp +++ b/rend2d/servers/sdl_driver.cpp @@ -7,6 +7,38 @@ using namespace Mangle::Rend2D; +// This is a really crappy and slow implementation +void SDL_Sprite::pixel(int x, int y, int color) +{ + SDL_LockSurface(surface); + + int bpp = surface->format->BytesPerPixel; + char *p = (char*)surface->pixels + y*surface->pitch + x*bpp; + + switch(bpp) + { + case 1: *p = color; break; + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) + { + p[0] = (color >> 16) & 0xff; + p[1] = (color >> 8) & 0xff; + p[2] = color & 0xff; + } + else + { + p[0] = color & 0xff; + p[1] = (color >> 8) & 0xff; + p[2] = (color >> 16) & 0xff; + } + break; + case 4: + *(int*)p = color; + break; + } + SDL_UnlockSurface(surface); +} + void SDL_Sprite::draw(Sprite *s, // Must be SDL_Sprite int x, int y, // Destination position int sx, int sy, // Source position diff --git a/rend2d/servers/sdl_driver.hpp b/rend2d/servers/sdl_driver.hpp index 9aeed2f92..4cab45c2e 100644 --- a/rend2d/servers/sdl_driver.hpp +++ b/rend2d/servers/sdl_driver.hpp @@ -34,6 +34,9 @@ namespace Mangle // Fill with a given pixel value void fill(int value); + // Set one pixel + void pixel(int x, int y, int value); + private: // The SDL surface SDL_Surface* surface; diff --git a/rend2d/sprite.hpp b/rend2d/sprite.hpp index 150d97e4e..7f6b9a220 100644 --- a/rend2d/sprite.hpp +++ b/rend2d/sprite.hpp @@ -28,6 +28,11 @@ namespace Mangle /// Fill the sprite with the given pixel value. The pixel format /// depends on the format of the sprite. virtual void fill(int value) = 0; + + /// Set one pixel value. The pixel format depends on the sprite + /// format. This is not expected to be fast, and in some + /// implementations may not work at all. + virtual void pixel(int x, int y, int value) {} }; } } From a05046026ec9edb1e528fac2c70f887239302237 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sun, 19 Sep 2010 12:36:19 +0200 Subject: [PATCH 107/111] More direct pixel functions. --- rend2d/servers/sdl_driver.cpp | 30 +++++++++++++++++++++++++++++- rend2d/servers/sdl_driver.hpp | 6 ++++++ rend2d/sprite.hpp | 18 ++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/rend2d/servers/sdl_driver.cpp b/rend2d/servers/sdl_driver.cpp index d86cf53e4..aa1ff6c6d 100644 --- a/rend2d/servers/sdl_driver.cpp +++ b/rend2d/servers/sdl_driver.cpp @@ -7,7 +7,34 @@ using namespace Mangle::Rend2D; -// This is a really crappy and slow implementation +const SpriteData *SDL_Sprite::lock() +{ + // Make sure we aren't already locked + assert(!data.pixels); + + // Lock the surface and set up the data structure + SDL_LockSurface(surface); + + data.pixels = surface->pixels; + data.w = surface->w; + data.h = surface->h; + data.pitch = surface->pitch; + data.bypp = surface->format->BytesPerPixel; + + return &data; +} + +void SDL_Sprite::unlock() +{ + if(data.pixels) + { + SDL_UnlockSurface(surface); + data.pixels = NULL; + } +} + +// This is a really crappy and slow implementation, only intended for +// testing purposes. Use lock/unlock for faster pixel drawing. void SDL_Sprite::pixel(int x, int y, int color) { SDL_LockSurface(surface); @@ -83,6 +110,7 @@ SDL_Sprite::SDL_Sprite(SDL_Surface *s, bool autoDelete) : surface(s), autoDel(autoDelete) { assert(surface != NULL); + data.pixels = NULL; } SDL_Sprite::~SDL_Sprite() diff --git a/rend2d/servers/sdl_driver.hpp b/rend2d/servers/sdl_driver.hpp index 4cab45c2e..0f205ba34 100644 --- a/rend2d/servers/sdl_driver.hpp +++ b/rend2d/servers/sdl_driver.hpp @@ -37,10 +37,16 @@ namespace Mangle // Set one pixel void pixel(int x, int y, int value); + const SpriteData *lock(); + void unlock(); + private: // The SDL surface SDL_Surface* surface; + // Used for locking + SpriteData data; + // If true, delete this surface when the canvas is destructed bool autoDel; }; diff --git a/rend2d/sprite.hpp b/rend2d/sprite.hpp index 7f6b9a220..f49da6cb6 100644 --- a/rend2d/sprite.hpp +++ b/rend2d/sprite.hpp @@ -5,6 +5,17 @@ namespace Mangle { namespace Rend2D { + /** + A pointer to sprite data for direct drawing. Only to be used + while the corresponding sprite is locked. + */ + struct SpriteData + { + void *pixels; // Pixel data + int w, h; // Width and height + int pitch, bypp; // Pitch (bytes) and bytes per pixel + }; + /** A Sprite is either a bitmap to be drawn or an output of area for blitting other bitmaps, or both. They are created by the @@ -33,6 +44,13 @@ namespace Mangle /// format. This is not expected to be fast, and in some /// implementations may not work at all. virtual void pixel(int x, int y, int value) {} + + /// Lock sprite for direct drawing, and return a struct + /// containing the necessary pointer. When finished, unlock the + /// sprite with unlock(). May return NULL, if so then direct + /// drawing is not possible. + virtual const SpriteData *lock() { return NULL; } + virtual void unlock() {} }; } } From 12e5c8d704b959c78a62931b2360d18092b82c3d Mon Sep 17 00:00:00 2001 From: Yuri Krupenin Date: Sat, 8 Jan 2011 23:42:51 +0300 Subject: [PATCH 108/111] Fix for audiere 1.9.4 (last non-SVN version). audiere.h uses cstring routines without including itself, this workaround should fix possible build errors. --- sound/sources/audiere_source.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/sources/audiere_source.hpp b/sound/sources/audiere_source.hpp index d0189b710..d797c55c8 100644 --- a/sound/sources/audiere_source.hpp +++ b/sound/sources/audiere_source.hpp @@ -3,6 +3,10 @@ #include "sample_reader.hpp" +// audiere.h from 1.9.4 (latest) release uses +// cstring routines like strchr() and strlen() without +// including cstring itself. +#include #include namespace Mangle { From 590f9e83d8158970dcbef1c5078a1a04cd8f1a59 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 22 Mar 2011 14:12:25 +0100 Subject: [PATCH 109/111] Windows-fix for gus --- sound/sources/audiere_source.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 4e5b6adce..faaa3c8c5 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -14,7 +14,8 @@ using namespace Mangle::Sound; // --- SampleSource --- -void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +void AudiereSource::getInfo(Mangle::Sound::int32_t *rate, + Mangle::Sound::int32_t *channels, Mangle::Sound::int32_t *bits) { SampleFormat fmt; int channels_, rate_; From f3c9694bf249a34eae05f0304e6bfc120014ce8c Mon Sep 17 00:00:00 2001 From: Jan-Peter Nilsson Date: Sun, 3 Apr 2011 13:06:42 +0200 Subject: [PATCH 110/111] Whitespace changes only --- rend2d/servers/sdl_gl_driver.cpp | 14 +++++++------- sound/sources/ffmpeg_source.cpp | 2 +- sound/sources/sample_reader.cpp | 2 +- stream/tests/buffer_filter_test.cpp | 2 +- stream/tests/iostream_test.cpp | 2 +- stream/tests/memory_server_test.cpp | 2 +- vfs/clients/ogre_archive.cpp | 2 +- vfs/clients/ogre_archive.hpp | 2 +- vfs/vfs.hpp | 4 ++-- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/rend2d/servers/sdl_gl_driver.cpp b/rend2d/servers/sdl_gl_driver.cpp index e2e829a7a..2bcb1d677 100644 --- a/rend2d/servers/sdl_gl_driver.cpp +++ b/rend2d/servers/sdl_gl_driver.cpp @@ -169,10 +169,10 @@ void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) // Have OpenGL generate a texture object handle for us glGenTextures( 1, &texture ); - + // Bind the texture object glBindTexture( GL_TEXTURE_2D, texture ); - + // Set the texture's stretching properties glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); @@ -213,24 +213,24 @@ void SDLGLDriver::updateNoSwap() // Bottom-left vertex (corner) glTexCoord2i( 0, 1 ); glVertex3f(0,diff,0); - + // Bottom-right vertex (corner) glTexCoord2i( 1, 1 ); glVertex3f( realDisp->w, diff, 0.f ); - + // Top-right vertex (corner) glTexCoord2i( 1, 0 ); glVertex3f( realDisp->w, screen->h, 0.f ); - + // Top-left vertex (corner) glTexCoord2i( 0, 0 ); glVertex3f( 0, screen->h, 0.f ); glEnd(); glMatrixMode(GL_PROJECTION); - glPopMatrix(); + glPopMatrix(); glMatrixMode(GL_MODELVIEW); - glPopMatrix(); + glPopMatrix(); } void SDLGLDriver::swap() diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index fdc2ff954..6349be691 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -34,7 +34,7 @@ FFMpegSource::FFMpegSource(const std::string &file) if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) fail("Error loading audio file " + file); - + if(av_find_stream_info(FmtCtx) < 0) { msg = "Error in file stream " + file; diff --git a/sound/sources/sample_reader.cpp b/sound/sources/sample_reader.cpp index fb4be9d5a..c30de654a 100644 --- a/sound/sources/sample_reader.cpp +++ b/sound/sources/sample_reader.cpp @@ -72,7 +72,7 @@ size_t SampleReader::read(void *_data, size_t length) // Determine how much we read return data-(char*)_data; } - + // Determine the overshoot pullSize = length - num; assert(pullSize < frameSize && pullSize >= 0); diff --git a/stream/tests/buffer_filter_test.cpp b/stream/tests/buffer_filter_test.cpp index 971530d84..e53bda651 100644 --- a/stream/tests/buffer_filter_test.cpp +++ b/stream/tests/buffer_filter_test.cpp @@ -36,6 +36,6 @@ int main() cout << "Result: " << data << endl; cout << "Eof: " << inp->eof() << endl; cout << "Pos: " << inp->tell() << endl; - + return 0; } diff --git a/stream/tests/iostream_test.cpp b/stream/tests/iostream_test.cpp index 6acebbbd6..60648c6c5 100644 --- a/stream/tests/iostream_test.cpp +++ b/stream/tests/iostream_test.cpp @@ -76,7 +76,7 @@ void test2() struct Dummy2 : Stream { - Dummy2() + Dummy2() { isWritable = true; isReadable = false; diff --git a/stream/tests/memory_server_test.cpp b/stream/tests/memory_server_test.cpp index 24dea79a2..24d3bb17e 100644 --- a/stream/tests/memory_server_test.cpp +++ b/stream/tests/memory_server_test.cpp @@ -37,6 +37,6 @@ int main() cout << "Pos: " << inp->tell() << endl; cout << "Entire stream from pointer: " << (char*)inp->getPtr() << endl; - + return 0; } diff --git a/vfs/clients/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp index ed6337053..2d3f7c520 100644 --- a/vfs/clients/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -71,7 +71,7 @@ Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern, return Ogre::StringVectorPtr(res); } -Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, +Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, bool recursive, bool dirs) { diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index 7cba6cfc8..7b371c6ef 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -50,7 +50,7 @@ class MangleArchive : public Ogre::Archive // Find functions will only work if vfs->hasFind is set. Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true, bool dirs = false); - Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, + Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, bool recursive = true, bool dirs = false); }; diff --git a/vfs/vfs.hpp b/vfs/vfs.hpp index ce51be94b..258a6b0a0 100644 --- a/vfs/vfs.hpp +++ b/vfs/vfs.hpp @@ -13,7 +13,7 @@ struct FileInfo { /// Full name, including path std::string name; - + /// Base name, not including path std::string basename; @@ -22,7 +22,7 @@ struct FileInfo /// File size size_t size; - + /// Last modification date time_t time; }; From 14b2851e72f610ae81dd296598867e6fb0babd2a Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Sat, 25 Feb 2012 19:09:45 +0200 Subject: [PATCH 111/111] Fix for cell change OpenAL music streaming issue --- sound/outputs/openal_out.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index bce08d02f..f1cf39838 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -30,8 +30,11 @@ static char tmp_buffer[BSIZE]; // Number of buffers used (per sound) for streaming sounds. Each // buffer is of size BSIZE. Increasing this will make streaming sounds // more fault tolerant against temporary lapses in call to update(), -// but will also increase memory usage. 4 should be ok. -const int STREAM_BUF_NUM = 4; +// but will also increase memory usage. +// This was changed from 4 to 150 for an estimated 30 seconds tolerance. +// At some point we should replace it with a more multithreading-ish +// solution. +const int STREAM_BUF_NUM = 150; static void fail(const std::string &msg) { throw std::runtime_error("OpenAL exception: " + msg); } @@ -101,7 +104,7 @@ class Mangle::Sound::OpenAL_Sound : public Sound ALuint inst; // Buffers. Only the first is used for non-streaming sounds. - ALuint bufferID[4]; + ALuint bufferID[STREAM_BUF_NUM]; // Number of buffers used int bufNum;