diff --git a/.gitignore b/.gitignore index 574a120a5..86c56816f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ Source/Info-gnustep.plist Tests/gui/*/GNUmakefile Tools/speech/GSSpeechServer.app Tools/speech_recognizer/GSSpeechRecognitionServer.app +Tools/sound/AudioOutput.nssound +Tools/sound/Sndfile.nssound *~ *.nssound *.nsmovie diff --git a/Headers/Additions/GNUstepGUI/GSVideoSink.h b/Headers/Additions/GNUstepGUI/GSVideoSink.h new file mode 100644 index 000000000..720c30ad2 --- /dev/null +++ b/Headers/Additions/GNUstepGUI/GSVideoSink.h @@ -0,0 +1,64 @@ +/* + GSVideoSink.h + + Sink video data. + + Copyright (C) 2022 Free Software Foundation, Inc. + + Written by: Gregory John Casamento + Date: Mar 2022 + + This file is part of the GNUstep GUI Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _GNUstep_H_GSVideoSink +#define _GNUstep_H_GSVideoSink + +#import +#import + +@protocol GSVideoSink + +/** + * Opens the device for output, called by [NSMovie-play]. + */ +- (BOOL) open; + +/** Closes the device, called by [NSMovie-stop]. + */ +- (void) close; + +/** + * Plays the data in bytes + */ +- (BOOL) playBytes: (void *)bytes length: (NSUInteger)length; + +/** Called by [NSMovieView -setVolume:], and corresponds to it. Parameter volume + * is between the values 0.0 and 1.0. + */ +- (void) setVolume: (float)volume; + +/** Called by [NSMovieView -volume]. + */ +- (CGFloat) volume; + +@end + +#endif // _GNUstep_H_GSVideoSink + diff --git a/Headers/Additions/GNUstepGUI/GSVideoSource.h b/Headers/Additions/GNUstepGUI/GSVideoSource.h new file mode 100644 index 000000000..a4f3893af --- /dev/null +++ b/Headers/Additions/GNUstepGUI/GSVideoSource.h @@ -0,0 +1,76 @@ +/* + GSVideoSource.h + + Load and read video data. + + Copyright (C) 2022 Free Software Foundation, Inc. + + Written by: Gregory John Casamento + Date: Mar 2022 + + This file is part of the GNUstep GUI Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _GNUstep_H_GSVideoSource +#define _GNUstep_H_GSVideoSource + +#import +#import + +@class NSArray; + +@protocol GSVideoSource + +/** Returns an array of the file types supported by the class. + */ ++ (NSArray *)videoUnfilteredFileTypes; + +/** Returns an array of UTIs identifying the file types the class understands. + */ ++ (NSArray *)videoUnfilteredTypes; + +/** Returns YES if the class can understand data and NO otherwise. + */ ++ (BOOL)canInitWithData: (NSData *)data; + +/** + * Initilizes the reciever for output. + */ +- (id)initWithData: (NSData *)data; + +/** Reads data provided in -initWithData:. Parameter bytes must be big enough + * to hold length bytes. + */ +- (NSUInteger)readBytes: (void *)bytes length: (NSUInteger)length; + +/** Returns the duration, in seconds. Equivalent to [NSMovieView-duration]. + */ +- (NSTimeInterval)duration; + +/** Called by [NSSound-setCurrentTime:]. + */ +- (void)setCurrentTime: (NSTimeInterval)currentTime; + +/** Called by [NSSound-currentTime]. + */ +- (NSTimeInterval)currentTime; + +@end + +#endif // _GNUstep_H_GSVideoSource diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index 84bc22815..908c25472 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -28,7 +28,8 @@ include ../config.make include ../Version -SUBPROJECTS = $(BUILD_SPEECH) $(BUILD_SOUND) $(BUILD_SPEECH_RECOGNIZER) +SUBPROJECTS = $(BUILD_SPEECH) $(BUILD_SOUND) $(BUILD_SPEECH_RECOGNIZER) \ + $(BUILD_VIDEO) TOOL_NAME = make_services set_show_service gopen gclose gcloseall SERVICE_NAME = GSspell diff --git a/Tools/video/GNUmakefile b/Tools/video/GNUmakefile new file mode 100644 index 000000000..bd0691ed2 --- /dev/null +++ b/Tools/video/GNUmakefile @@ -0,0 +1,31 @@ +PACKAGE_NAME = gnustep-gui +include $(GNUSTEP_MAKEFILES)/common.make + +BUNDLE_NAME = VideoFile VideoOutput +BUNDLE_EXTENSION = .nsmovie + +# These are here in case GNUstep is not installed, +# otherwise compilation will fail. +VideoFile_INCLUDE_DIRS += -I../../Headers \ + -I../../Headers/Additions + +VideoFile_LIB_DIRS += \ + -L../../Source/$(GNUSTEP_OBJ_DIR) + +VideoOutput_INCLUDE_DIRS += -I../../Headers \ + -I../../Headers/Additions + +VideoOutput_LIB_DIRS += \ + -L../../Source/$(GNUSTEP_OBJ_DIR) + +# Build the bundles. +VideoFile_OBJC_FILES = VideoFileSource.m +VideoOutput_OBJC_FILES = VideoOutputSink.m + +VideoFile_PRINCIPAL_CLASS = VideoFileSource +VideoOutput_PRINCIPAL_CLASS = VidioOutputSink + +VideoFile_BUNDLE_LIBS = +VideoOutput_BUNDLE_LIBS = + +include $(GNUSTEP_MAKEFILES)/bundle.make diff --git a/Tools/video/VideoFile.nsmovie/Resources/Info-gnustep.plist b/Tools/video/VideoFile.nsmovie/Resources/Info-gnustep.plist new file mode 100644 index 000000000..11ce77dc5 --- /dev/null +++ b/Tools/video/VideoFile.nsmovie/Resources/Info-gnustep.plist @@ -0,0 +1,6 @@ +{ + NOTE = "Automatically generated, do not edit!"; + NSExecutable = "VideoFile"; + NSMainNibFile = ""; + NSPrincipalClass = "VideoFileSource"; +} diff --git a/Tools/video/VideoFile.nsmovie/VideoFile b/Tools/video/VideoFile.nsmovie/VideoFile new file mode 100755 index 000000000..cbc8af3b8 Binary files /dev/null and b/Tools/video/VideoFile.nsmovie/VideoFile differ diff --git a/Tools/video/VideoFile.nsmovie/stamp.make b/Tools/video/VideoFile.nsmovie/stamp.make new file mode 100644 index 000000000..69115ae58 --- /dev/null +++ b/Tools/video/VideoFile.nsmovie/stamp.make @@ -0,0 +1 @@ +OLD_GNUSTEP_STAMP_ASTRING = _VideoFileSource- diff --git a/Tools/video/VideoFileSource.m b/Tools/video/VideoFileSource.m new file mode 100644 index 000000000..70c47edca --- /dev/null +++ b/Tools/video/VideoFileSource.m @@ -0,0 +1,240 @@ +/* + SndfileSource.m + + Load and read sound data using libsndfile. + + Copyright (C) 2009 Free Software Foundation, Inc. + + Written by: Stefan Bidigaray + Date: Jun 2009 + + This file is part of the GNUstep GUI Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include "GNUstepGUI/GSSoundSource.h" +#include + +@interface SndfileSource : NSObject +{ + NSData *_data; + SNDFILE *_snd; + SF_INFO _info; + + NSUInteger _curPos; + NSTimeInterval _dur; + int _encoding; +} + +- (NSData *)data; +- (NSUInteger)currentPosition; +- (void)setCurrentPosition: (NSUInteger)curPos; +@end + +/**********************************/ +/* Sndfile virtual I/O functions. */ +/**********************************/ +static inline sf_count_t dataLength (void *user_data) +{ + SndfileSource *snd = (SndfileSource *)user_data; + + return (sf_count_t)[[snd data] length]; +} + +static inline sf_count_t dataSeek (sf_count_t offset, int whence, + void *user_data) +{ + SndfileSource *snd = (SndfileSource *)user_data; + + switch (whence) + { + case SEEK_SET: + break; + case SEEK_END: + offset = (sf_count_t)[[snd data] length] + offset; + break; + case SEEK_CUR: + offset = (sf_count_t)[snd currentPosition] + offset; + break; + default: + return 0; + } + [snd setCurrentPosition: (NSUInteger)offset]; + return (sf_count_t)[snd currentPosition]; +} + +static inline sf_count_t dataRead (void *ptr, sf_count_t count, + void *user_data) +{ + NSUInteger newPos; + SndfileSource *snd = (SndfileSource *)user_data; + + // Can't read more data that we have available... + if (([snd currentPosition] + (NSUInteger)count) > [[snd data] length]) + { + count = (sf_count_t)([[snd data] length] - [snd currentPosition]); + } + + newPos = [snd currentPosition] + (NSUInteger)count; + [[snd data] getBytes: ptr + range: NSMakeRange ([snd currentPosition], count)]; + [snd setCurrentPosition: newPos]; + + return count; +} + +static inline sf_count_t dataWrite (const void *ptr, sf_count_t count, + void *user_data) +{ + /* FIXME: No write support... do we even need it? */ + return 0; +} + +static inline sf_count_t dataTell (void *user_data) +{ + SndfileSource *snd = (SndfileSource *)user_data; + return (sf_count_t)[snd currentPosition]; +} + +// The libsndfile virtual I/O function structure +static SF_VIRTUAL_IO dataIO = { (sf_vio_get_filelen)dataLength, + (sf_vio_seek)dataSeek, + (sf_vio_read)dataRead, + (sf_vio_write)dataWrite, + (sf_vio_tell)dataTell }; +/**********************************/ + +@implementation SndfileSource + ++ (NSArray *)soundUnfilteredFileTypes +{ + return [NSArray arrayWithObjects: @"wav", @"au", @"snd", @"aif", @"aiff", + @"aifc", @"paf", @"sf", @"voc", @"w64", @"mat", @"mat4", @"mat5", + @"pcf", @"xi", @"caf", @"sd2", @"iff", @"flac", @"ogg", @"oga", + nil]; +} ++ (NSArray *)soundUnfilteredTypes +{ + /* FIXME: I'm not sure what the UTI for all the types above are. */ + return [NSArray arrayWithObjects: @"com.microsoft.waveform-audio", + @"public.ulaw-audio", @"public.aiff-audio", @"public.aifc-audio", + @"com.apple.coreaudio-format", @"com.digidesign.sd2-audio", + /* FIXME: are these right? */ + @"org.xiph.flac-audio", @"org.xiph.vorbis-audio", nil]; +} ++ (BOOL)canInitWithData: (NSData *)data +{ + return YES; +} + +- (void)dealloc +{ + TEST_RELEASE (_data); + sf_close (_snd); + + [super dealloc]; +} + +- (id)initWithData: (NSData *)data +{ + self = [super init]; + if (self == nil) + { + return nil; + } + + _data = data; + RETAIN(_data); + + _info.format = 0; + _snd = sf_open_virtual (&dataIO, SFM_READ, &_info, self); + if (_snd == NULL) + { + DESTROY(self); + return nil; + } + + // Setup immutable values... + /* FIXME: support multiple types */ + _encoding = GSSoundFormatPCM16; + _dur = (double)_info.frames / (double)_info.samplerate; + + return self; +} + +- (NSUInteger)readBytes: (void *)bytes length: (NSUInteger)length +{ + return (NSUInteger) (sf_read_short (_snd, bytes, (length>>1))<<1); +} + +- (NSTimeInterval)duration +{ + return _dur; +} + +- (void)setCurrentTime: (NSTimeInterval)currentTime +{ + sf_count_t frames = (sf_count_t)((double)_info.samplerate * currentTime); + sf_seek (_snd, frames, SEEK_SET); +} +- (NSTimeInterval)currentTime +{ + sf_count_t frames; + frames = sf_seek (_snd, 0, SEEK_CUR); + return (NSTimeInterval)((double)frames / (double)_info.samplerate); +} + +- (int)encoding +{ + return _encoding; +} + +- (NSUInteger)channelCount +{ + return (NSUInteger)_info.channels; +} + +- (NSUInteger)sampleRate; +{ + return (NSUInteger)_info.samplerate; +} + +- (NSByteOrder)byteOrder +{ + // Equivalent to sending native byte order... + // Sndfile always reads as native format. + return NS_UnknownByteOrder; +} + +- (NSData *)data +{ + return _data; +} + +- (NSUInteger)currentPosition +{ + return _curPos; +} + +- (void)setCurrentPosition: (NSUInteger)curPos +{ + _curPos = curPos; +} + +@end diff --git a/Tools/video/VideoOutput.nsmovie/Resources/Info-gnustep.plist b/Tools/video/VideoOutput.nsmovie/Resources/Info-gnustep.plist new file mode 100644 index 000000000..ebcde1375 --- /dev/null +++ b/Tools/video/VideoOutput.nsmovie/Resources/Info-gnustep.plist @@ -0,0 +1,6 @@ +{ + NOTE = "Automatically generated, do not edit!"; + NSExecutable = "VideoOutput"; + NSMainNibFile = ""; + NSPrincipalClass = "VidioOutputSink"; +} diff --git a/Tools/video/VideoOutput.nsmovie/VideoOutput b/Tools/video/VideoOutput.nsmovie/VideoOutput new file mode 100755 index 000000000..9f6bcbf29 Binary files /dev/null and b/Tools/video/VideoOutput.nsmovie/VideoOutput differ diff --git a/Tools/video/VideoOutput.nsmovie/stamp.make b/Tools/video/VideoOutput.nsmovie/stamp.make new file mode 100644 index 000000000..387d63845 --- /dev/null +++ b/Tools/video/VideoOutput.nsmovie/stamp.make @@ -0,0 +1 @@ +OLD_GNUSTEP_STAMP_ASTRING = _VidioOutputSink- diff --git a/Tools/video/VideoOutputSink.m b/Tools/video/VideoOutputSink.m new file mode 100644 index 000000000..453dc48a3 --- /dev/null +++ b/Tools/video/VideoOutputSink.m @@ -0,0 +1,170 @@ +/* + AudioOutputSink.m + + Sink audio data to libao. + + Copyright (C) 2009 Free Software Foundation, Inc. + + Written by: Stefan Bidigaray + Date: Jun 2009 + + This file is part of the GNUstep GUI Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include + +@interface AudioOutputSink : NSObject +{ + ao_device *_dev; + int _driver; + ao_sample_format _format; +} +@end + +@implementation AudioOutputSink + ++ (void) initialize +{ + /* FIXME: According to the docs, this needs a corresponding ao_shutdown(). */ + ao_initialize (); +} + ++ (BOOL)canInitWithPlaybackDevice: (NSString *)playbackDevice +{ + // This is currently the only sink in NSSound, just say + // YES to everything. + /* FIXME: What is OS X's identifier for the main sound? */ + return (playbackDevice == nil ? YES : NO); +} + +- (void)dealloc +{ + [super dealloc]; +} + +- (id)initWithEncoding: (int)encoding + channels: (NSUInteger)channelCount + sampleRate: (NSUInteger)sampleRate + byteOrder: (NSByteOrder)byteOrder +{ + self = [super init]; + if (self == nil) + { + return nil; + } + + _format.channels = (int)channelCount; + _format.rate = (int)sampleRate; + + switch (encoding) + { + case GSSoundFormatPCMS8: + _format.bits = 8; + break; + + case GSSoundFormatPCM16: + _format.bits = 16; + break; + + case GSSoundFormatPCM24: + _format.bits = 24; + break; + + case GSSoundFormatPCM32: + _format.bits = 32; + break; + + case GSSoundFormatFloat32: // Float and double not supported by libao. + case GSSoundFormatFloat64: + default: + DESTROY(self); + return nil; + } + + if (byteOrder == NS_LittleEndian) + { + _format.byte_format = AO_FMT_LITTLE; + } + else if (byteOrder == NS_BigEndian) + { + _format.byte_format = AO_FMT_BIG; + } + else + { + _format.byte_format = AO_FMT_NATIVE; + } + + return self; +} + +- (BOOL)open +{ + _driver = ao_default_driver_id(); + + _dev = ao_open_live(_driver, &_format, NULL); + return ((_dev == NULL) ? NO : YES); +} + +- (void)close +{ + ao_close(_dev); +} + +- (BOOL)playBytes: (void *)bytes length: (NSUInteger)length +{ + int ret = ao_play(_dev, bytes, (uint_32)length); + return (ret == 0 ? NO : YES); +} + +/* Functionality not supported by libao */ +- (void)setVolume: (float)volume +{ + return; +} + +- (float)volume +{ + return 1.0; +} + +- (void)setPlaybackDeviceIdentifier: (NSString *)playbackDeviceIdentifier +{ + return; +} + +- (NSString *)playbackDeviceIdentifier +{ + return nil; +} + +- (void)setChannelMapping: (NSArray *)channelMapping +{ + return; +} + +- (NSArray *)channelMapping +{ + return nil; +} + +@end + diff --git a/config.make.in b/config.make.in index f948caa0b..873583c01 100644 --- a/config.make.in +++ b/config.make.in @@ -13,6 +13,7 @@ BUILD_SOUND=@BUILD_SOUND@ RECOGNIZER_BASE_LIBS=@RECOGNIZER_BASE_LIBS@ RECOGNIZER_BASE_CFLAGS=@RECOGNIZER_BASE_CFLAGS@ RECOGNIZER_ENGINE_CLASS=@RECOGNIZER_ENGINE_CLASS@ +BUILD_VIDEO=@BUILD_VIDEO@ # CUPS GSCUPS_CFLAGS = @GSCUPS_CFLAGS@ diff --git a/configure b/configure index 40c27cd89..d5b52dfb3 100755 --- a/configure +++ b/configure @@ -635,6 +635,7 @@ GSCUPS_LIBS GSCUPS_LDFLAGS GSCUPS_CFLAGS have_cups +BUILD_VIDEO RECOGNIZER_ENGINE_CLASS RECOGNIZER_BASE_CFLAGS RECOGNIZER_BASE_LIBS @@ -734,6 +735,7 @@ enable_aspell enable_sound enable_speech enable_speech_recognizer +enable_video enable_cups ' ac_precious_vars='build_alias @@ -1390,6 +1392,7 @@ Optional Features: --disable-sound Disable sound --disable-speech Disable speech server --disable-speech-recognizer Disable speech recognition server + --disable-video Disable video --disable-cups Disable cups printing support Optional Packages: @@ -5748,6 +5751,40 @@ fi +#-------------------------------------------------------------------- +# NSMovie +#-------------------------------------------------------------------- +# Check whether --enable-video was given. +if test "${enable_video+set}" = set; then : + enableval=$enable_video; +else + enable_video=yes +fi + +# Initialize to nothing... +BUILD_VIDEO= + +# Check for the headers... +for ac_header in libavcodec/avcodec.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "libavcodec/avcodec.h" "ac_cv_header_libavcodec_avcodec_h" "$ac_includes_default" +if test "x$ac_cv_header_libavcodec_avcodec_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBAVCODEC_AVCODEC_H 1 +_ACEOF + have_video=yes +else + have_video=no +fi + +done + +# Only if we have both... +if test $have_video = yes -a $enable_video = yes; then + BUILD_VIDEO="video" +fi + + #-------------------------------------------------------------------- # Find CUPS #-------------------------------------------------------------------- diff --git a/configure.ac b/configure.ac index 078a462ba..7b1178841 100644 --- a/configure.ac +++ b/configure.ac @@ -560,6 +560,23 @@ AC_SUBST(RECOGNIZER_BASE_LIBS) AC_SUBST(RECOGNIZER_BASE_CFLAGS) AC_SUBST(RECOGNIZER_ENGINE_CLASS) +#-------------------------------------------------------------------- +# NSMovie +#-------------------------------------------------------------------- +AC_ARG_ENABLE(video, + [ --disable-video Disable video],, + enable_video=yes) +# Initialize to nothing... +BUILD_VIDEO= + +# Check for the headers... +AC_CHECK_HEADERS(libavcodec/avcodec.h, have_video=yes, have_video=no) +# Only if we have both... +if test $have_video = yes -a $enable_video = yes; then + BUILD_VIDEO="video" +fi +AC_SUBST(BUILD_VIDEO) + #-------------------------------------------------------------------- # Find CUPS #--------------------------------------------------------------------