hornetseye-fftw3 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +1 -0
- data/COPYING +679 -0
- data/README.md +28 -0
- data/Rakefile +183 -0
- data/ext/error.hh +50 -0
- data/ext/init.cc +38 -0
- data/ext/node.cc +223 -0
- data/ext/node.hh +33 -0
- data/ext/rubyinc.hh +54 -0
- data/lib/hornetseye-fftw3/node.rb +52 -0
- data/lib/hornetseye_fftw3_ext.rb +18 -0
- data/test/tc_fftw3.rb +58 -0
- data/test/ts_fftw3.rb +18 -0
- metadata +117 -0
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
hornetseye-fftw3
|
2
|
+
================
|
3
|
+
|
4
|
+
**Author**: Jan Wedekind
|
5
|
+
**Copyright**: 2011
|
6
|
+
**License**: GPL
|
7
|
+
|
8
|
+
Synopsis
|
9
|
+
--------
|
10
|
+
|
11
|
+
This Ruby extension provides bindings for the FFTW3 library.
|
12
|
+
|
13
|
+
Installation
|
14
|
+
------------
|
15
|
+
|
16
|
+
*hornetseye-fftw3* requires the FFTW3 libraries. If you are running Debian or (K)ubuntu, you can install them like this:
|
17
|
+
|
18
|
+
$ sudo aptitude install libfftw3-dev
|
19
|
+
|
20
|
+
To install this Ruby extension, use the following command:
|
21
|
+
|
22
|
+
$ sudo gem install hornetseye-fftw3
|
23
|
+
|
24
|
+
Alternatively you can build and install the Ruby extension from source as follows:
|
25
|
+
|
26
|
+
$ rake
|
27
|
+
$ sudo rake install
|
28
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'date'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/loaders/makefile'
|
7
|
+
require 'rbconfig'
|
8
|
+
require 'tempfile'
|
9
|
+
|
10
|
+
PKG_NAME = 'hornetseye-fftw3'
|
11
|
+
PKG_VERSION = '0.1.0'
|
12
|
+
CFG = RbConfig::CONFIG
|
13
|
+
CXX = ENV[ 'CXX' ] || 'g++'
|
14
|
+
RB_FILES = FileList[ 'lib/**/*.rb' ]
|
15
|
+
CC_FILES = FileList[ 'ext/*.cc' ]
|
16
|
+
HH_FILES = FileList[ 'ext/*.hh' ] + FileList[ 'ext/*.tcc' ]
|
17
|
+
TC_FILES = FileList[ 'test/tc_*.rb' ]
|
18
|
+
TS_FILES = FileList[ 'test/ts_*.rb' ]
|
19
|
+
SO_FILE = "ext/#{PKG_NAME.tr '\-', '_'}.#{CFG[ 'DLEXT' ]}"
|
20
|
+
PKG_FILES = [ 'Rakefile', 'README.md', 'COPYING', '.document' ] +
|
21
|
+
RB_FILES + CC_FILES + HH_FILES + TS_FILES + TC_FILES
|
22
|
+
BIN_FILES = [ 'README.md', 'COPYING', '.document', SO_FILE ] +
|
23
|
+
RB_FILES + TS_FILES + TC_FILES
|
24
|
+
SUMMARY = %q{Fourier transforms}
|
25
|
+
DESCRIPTION = %q{This Ruby extension provides bindings for the FFTW3 library.}
|
26
|
+
AUTHOR = %q{Jan Wedekind}
|
27
|
+
EMAIL = %q{jan@wedesoft.de}
|
28
|
+
HOMEPAGE = %q{http://wedesoft.github.com/hornetseye-fftw3/}
|
29
|
+
|
30
|
+
OBJ = CC_FILES.ext 'o'
|
31
|
+
$CXXFLAGS = "-DNDEBUG -DHAVE_CONFIG_H -D__STDC_CONSTANT_MACROS #{CFG[ 'CPPFLAGS' ]} #{CFG[ 'CFLAGS' ]}"
|
32
|
+
if CFG[ 'rubyhdrdir' ]
|
33
|
+
$CXXFLAGS = "#{$CXXFLAGS} -I#{CFG[ 'rubyhdrdir' ]} " +
|
34
|
+
"-I#{CFG[ 'rubyhdrdir' ]}/#{CFG[ 'arch' ]}"
|
35
|
+
else
|
36
|
+
$CXXFLAGS = "#{$CXXFLAGS} -I#{CFG[ 'archdir' ]}"
|
37
|
+
end
|
38
|
+
$LIBRUBYARG = "-L#{CFG[ 'libdir' ]} #{CFG[ 'LIBRUBYARG' ]} #{CFG[ 'LDFLAGS' ]} " +
|
39
|
+
"#{CFG[ 'SOLIBS' ]} #{CFG[ 'DLDLIBS' ]}"
|
40
|
+
$SITELIBDIR = CFG[ 'sitelibdir' ]
|
41
|
+
$SITEARCHDIR = CFG[ 'sitearchdir' ]
|
42
|
+
$LDSHARED = CFG[ 'LDSHARED' ][ CFG[ 'LDSHARED' ].index( ' ' ) .. -1 ]
|
43
|
+
|
44
|
+
task :default => :all
|
45
|
+
|
46
|
+
desc 'Compile Ruby extension (default)'
|
47
|
+
task :all => [ SO_FILE ]
|
48
|
+
|
49
|
+
file SO_FILE => OBJ do |t|
|
50
|
+
sh "#{CXX} -shared -o #{t.name} #{OBJ} -lfftw3 -lfftw3f #{$LIBRUBYARG}"
|
51
|
+
end
|
52
|
+
|
53
|
+
task :test => [ SO_FILE ]
|
54
|
+
|
55
|
+
desc 'Install Ruby extension'
|
56
|
+
task :install => :all do
|
57
|
+
verbose true do
|
58
|
+
for f in RB_FILES do
|
59
|
+
FileUtils.mkdir_p "#{$SITELIBDIR}/#{File.dirname f.gsub( /^lib\//, '' )}"
|
60
|
+
FileUtils.cp_r f, "#{$SITELIBDIR}/#{f.gsub( /^lib\//, '' )}"
|
61
|
+
end
|
62
|
+
FileUtils.mkdir_p $SITEARCHDIR
|
63
|
+
FileUtils.cp SO_FILE, "#{$SITEARCHDIR}/#{File.basename SO_FILE}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
desc 'Uninstall Ruby extension'
|
68
|
+
task :uninstall do
|
69
|
+
verbose true do
|
70
|
+
for f in RB_FILES do
|
71
|
+
FileUtils.rm_f "#{$SITELIBDIR}/#{f.gsub /^lib\//, ''}"
|
72
|
+
end
|
73
|
+
FileUtils.rm_f "#{$SITEARCHDIR}/#{File.basename SO_FILE}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
Rake::TestTask.new do |t|
|
78
|
+
t.libs << 'ext'
|
79
|
+
t.test_files = TC_FILES
|
80
|
+
end
|
81
|
+
|
82
|
+
begin
|
83
|
+
require 'yard'
|
84
|
+
YARD::Rake::YardocTask.new :yard do |y|
|
85
|
+
y.files << RB_FILES
|
86
|
+
end
|
87
|
+
rescue LoadError
|
88
|
+
STDERR.puts 'Please install \'yard\' if you want to generate documentation'
|
89
|
+
end
|
90
|
+
|
91
|
+
Rake::PackageTask.new PKG_NAME, PKG_VERSION do |p|
|
92
|
+
p.need_tar = true
|
93
|
+
p.package_files = PKG_FILES
|
94
|
+
end
|
95
|
+
|
96
|
+
begin
|
97
|
+
require 'rubygems'
|
98
|
+
require 'rubygems/builder'
|
99
|
+
$SPEC = Gem::Specification.new do |s|
|
100
|
+
s.name = PKG_NAME
|
101
|
+
s.version = PKG_VERSION
|
102
|
+
s.platform = Gem::Platform::RUBY
|
103
|
+
s.date = Date.today.to_s
|
104
|
+
s.summary = SUMMARY
|
105
|
+
s.description = DESCRIPTION
|
106
|
+
s.author = AUTHOR
|
107
|
+
s.email = EMAIL
|
108
|
+
s.homepage = HOMEPAGE
|
109
|
+
s.files = PKG_FILES
|
110
|
+
s.test_files = TC_FILES
|
111
|
+
s.require_paths = [ 'lib', 'ext' ]
|
112
|
+
s.rubyforge_project = %q{hornetseye}
|
113
|
+
s.extensions = %w{Rakefile}
|
114
|
+
s.has_rdoc = 'yard'
|
115
|
+
s.extra_rdoc_files = []
|
116
|
+
s.rdoc_options = %w{--no-private}
|
117
|
+
s.add_dependency %<malloc>, [ '~> 1.1' ]
|
118
|
+
s.add_dependency %<multiarray>, [ '~> 0.20' ]
|
119
|
+
s.add_development_dependency %q{rake}
|
120
|
+
end
|
121
|
+
GEM_SOURCE = "#{PKG_NAME}-#{PKG_VERSION}.gem"
|
122
|
+
$BINSPEC = Gem::Specification.new do |s|
|
123
|
+
s.name = PKG_NAME
|
124
|
+
s.version = PKG_VERSION
|
125
|
+
s.platform = Gem::Platform::CURRENT
|
126
|
+
s.date = Date.today.to_s
|
127
|
+
s.summary = SUMMARY
|
128
|
+
s.description = DESCRIPTION
|
129
|
+
s.author = AUTHOR
|
130
|
+
s.email = EMAIL
|
131
|
+
s.homepage = HOMEPAGE
|
132
|
+
s.files = BIN_FILES
|
133
|
+
s.test_files = TC_FILES
|
134
|
+
s.require_paths = [ 'lib', 'ext' ]
|
135
|
+
s.rubyforge_project = %q{hornetseye}
|
136
|
+
s.has_rdoc = 'yard'
|
137
|
+
s.extra_rdoc_files = []
|
138
|
+
s.rdoc_options = %w{--no-private}
|
139
|
+
s.add_dependency %<malloc>, [ '~> 1.1' ]
|
140
|
+
s.add_dependency %<multiarray>, [ '~> 0.20' ]
|
141
|
+
end
|
142
|
+
GEM_BINARY = "#{PKG_NAME}-#{PKG_VERSION}-#{$BINSPEC.platform}.gem"
|
143
|
+
desc "Build the gem file #{GEM_SOURCE}"
|
144
|
+
task :gem => [ "pkg/#{GEM_SOURCE}" ]
|
145
|
+
file "pkg/#{GEM_SOURCE}" => [ 'pkg' ] + $SPEC.files do
|
146
|
+
when_writing 'Creating GEM' do
|
147
|
+
Gem::Builder.new( $SPEC ).build
|
148
|
+
verbose true do
|
149
|
+
FileUtils.mv GEM_SOURCE, "pkg/#{GEM_SOURCE}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
desc "Build the gem file #{GEM_BINARY}"
|
154
|
+
task :gem_binary => [ "pkg/#{GEM_BINARY}" ]
|
155
|
+
file "pkg/#{GEM_BINARY}" => [ 'pkg' ] + $BINSPEC.files do
|
156
|
+
when_writing 'Creating binary GEM' do
|
157
|
+
Gem::Builder.new( $BINSPEC ).build
|
158
|
+
verbose true do
|
159
|
+
FileUtils.mv GEM_BINARY, "pkg/#{GEM_BINARY}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
rescue LoadError
|
164
|
+
STDERR.puts 'Please install \'rubygems\' if you want to create Gem packages'
|
165
|
+
end
|
166
|
+
|
167
|
+
rule '.o' => '.cc' do |t|
|
168
|
+
sh "#{CXX} #{$CXXFLAGS} -c -o #{t.name} #{t.source}"
|
169
|
+
end
|
170
|
+
|
171
|
+
file ".depends.mf" do |t|
|
172
|
+
sh "g++ -MM #{CC_FILES.join ' '} | " +
|
173
|
+
"sed -e :a -e N -e 's/\\n/\\$/g' -e ta | " +
|
174
|
+
"sed -e 's/ *\\\\\\$ */ /g' -e 's/\\$/\\n/g' | sed -e 's/^/ext\\//' > #{t.name}"
|
175
|
+
end
|
176
|
+
CC_FILES.each do |t|
|
177
|
+
file t.ext(".o") => t
|
178
|
+
end
|
179
|
+
import ".depends.mf"
|
180
|
+
|
181
|
+
CLEAN.include 'ext/*.o'
|
182
|
+
CLOBBER.include SO_FILE, 'doc', '.yardoc', '.depends.mf'
|
183
|
+
|
data/ext/error.hh
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
/* HornetsEye - Computer Vision with Ruby
|
2
|
+
Copyright (C) 2006, 2007, 2008, 2009, 2010 Jan Wedekind
|
3
|
+
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
|
+
#ifndef ERROR_HH
|
17
|
+
#define ERROR_HH
|
18
|
+
|
19
|
+
#include <exception>
|
20
|
+
#include <sstream>
|
21
|
+
|
22
|
+
class Error: public std::exception
|
23
|
+
{
|
24
|
+
public:
|
25
|
+
Error(void) {}
|
26
|
+
Error( Error &e ): std::exception( e )
|
27
|
+
{ m_message << e.m_message.str(); }
|
28
|
+
virtual ~Error(void) throw() {}
|
29
|
+
template< typename T >
|
30
|
+
std::ostream &operator<<( const T &t )
|
31
|
+
{ m_message << t; return m_message; }
|
32
|
+
std::ostream &operator<<( std::ostream& (*__pf)(std::ostream&) )
|
33
|
+
{ (*__pf)( m_message ); return m_message; }
|
34
|
+
virtual const char* what(void) const throw() {
|
35
|
+
temp = m_message.str();
|
36
|
+
return temp.c_str();
|
37
|
+
}
|
38
|
+
protected:
|
39
|
+
std::ostringstream m_message;
|
40
|
+
mutable std::string temp;
|
41
|
+
};
|
42
|
+
|
43
|
+
#define ERRORMACRO( condition, class, params, message ) \
|
44
|
+
if ( !( condition ) ) { \
|
45
|
+
class _e params; \
|
46
|
+
_e << message; \
|
47
|
+
throw _e; \
|
48
|
+
};
|
49
|
+
|
50
|
+
#endif
|
data/ext/init.cc
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
/* HornetsEye - Computer Vision with Ruby
|
2
|
+
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Jan Wedekind
|
3
|
+
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
|
+
#include "rubyinc.hh"
|
17
|
+
#include "node.hh"
|
18
|
+
|
19
|
+
#ifdef WIN32
|
20
|
+
#define DLLEXPORT __declspec(dllexport)
|
21
|
+
#define DLLLOCAL
|
22
|
+
#else
|
23
|
+
#define DLLEXPORT __attribute__ ((visibility("default")))
|
24
|
+
#define DLLLOCAL __attribute__ ((visibility("hidden")))
|
25
|
+
#endif
|
26
|
+
|
27
|
+
extern "C" DLLEXPORT void Init_hornetseye_fftw3(void);
|
28
|
+
|
29
|
+
extern "C" {
|
30
|
+
|
31
|
+
void Init_hornetseye_fftw3(void)
|
32
|
+
{
|
33
|
+
VALUE rbHornetseye = rb_define_module( "Hornetseye" );
|
34
|
+
Node::registerRubyClass( rbHornetseye );
|
35
|
+
rb_require( "hornetseye_fftw3_ext.rb" );
|
36
|
+
}
|
37
|
+
|
38
|
+
}
|
data/ext/node.cc
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
/* HornetsEye - Computer Vision with Ruby
|
2
|
+
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Jan Wedekind
|
3
|
+
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
16
|
+
#include <iostream>
|
17
|
+
|
18
|
+
#include <boost/shared_array.hpp>
|
19
|
+
#include <fftw3.h>
|
20
|
+
#include "error.hh"
|
21
|
+
#include "node.hh"
|
22
|
+
|
23
|
+
using namespace boost;
|
24
|
+
using namespace std;
|
25
|
+
|
26
|
+
VALUE Node::cRubyClass = Qnil;
|
27
|
+
|
28
|
+
VALUE Node::mModule = Qnil;
|
29
|
+
|
30
|
+
VALUE Node::registerRubyClass( VALUE module )
|
31
|
+
{
|
32
|
+
mModule = module;
|
33
|
+
cRubyClass = rb_define_class_under( module, "Node", rb_cObject );
|
34
|
+
rb_define_method( cRubyClass, "fft", RUBY_METHOD_FUNC( wrapFFT ), 1 );
|
35
|
+
rb_define_method( cRubyClass, "rfft", RUBY_METHOD_FUNC( wrapRFFT ), 1 );
|
36
|
+
return cRubyClass;
|
37
|
+
}
|
38
|
+
|
39
|
+
VALUE Node::wrapFFT( VALUE rbSelf, VALUE rbForward )
|
40
|
+
{
|
41
|
+
VALUE rbRetVal = Qnil;
|
42
|
+
try {
|
43
|
+
int sign = rbForward == Qtrue ? FFTW_FORWARD : FFTW_BACKWARD;
|
44
|
+
VALUE rbMalloc = rb_funcall( rbSelf, rb_intern( "memory" ), 0 );
|
45
|
+
VALUE rbTypecode = rb_funcall( rbSelf, rb_intern( "typecode" ), 0 );
|
46
|
+
VALUE rbSize = rb_funcall( rbSelf, rb_intern( "size" ), 0 );
|
47
|
+
int size = NUM2INT( rbSize );
|
48
|
+
VALUE rbShape = rb_funcall( rbSelf, rb_intern( "shape" ), 0 );
|
49
|
+
int rank = RARRAY_LEN( rbShape );
|
50
|
+
shared_array< int > n( new int[ rank ] );
|
51
|
+
for ( int i=0; i<rank; i++ )
|
52
|
+
n[ rank - 1 - i ] = NUM2INT( RARRAY_PTR( rbShape )[i] );
|
53
|
+
VALUE rbDest = Qnil;
|
54
|
+
if ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
|
55
|
+
rb_const_get( mModule, rb_intern( "SCOMPLEX" ) ) ) == Qtrue ) {
|
56
|
+
assert( sizeof( fftwf_complex ) == sizeof( float ) * 2 );
|
57
|
+
fftwf_complex *source;
|
58
|
+
Data_Get_Struct( rbMalloc, fftwf_complex, source );
|
59
|
+
fftwf_complex *dest = ALLOC_N( fftwf_complex, size );
|
60
|
+
rbDest = Data_Wrap_Struct( rb_const_get( mModule, rb_intern( "Malloc" ) ), 0, xfree,
|
61
|
+
(void *)dest );
|
62
|
+
rb_ivar_set( rbDest, rb_intern( "@size" ), INT2NUM( size * sizeof( fftwf_complex ) ) );
|
63
|
+
fftwf_plan plan =
|
64
|
+
fftwf_plan_dft( rank, n.get(), source, dest, sign,
|
65
|
+
FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
|
66
|
+
ERRORMACRO( plan != NULL, Error, , "Error creating FFTW plan" );
|
67
|
+
fftwf_execute( plan );
|
68
|
+
fftwf_destroy_plan( plan );
|
69
|
+
} else if ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
|
70
|
+
rb_const_get( mModule, rb_intern( "DCOMPLEX" ) ) ) == Qtrue ) {
|
71
|
+
assert( sizeof( fftw_complex ) == sizeof( double ) * 2 );
|
72
|
+
fftw_complex *source;
|
73
|
+
Data_Get_Struct( rbMalloc, fftw_complex, source );
|
74
|
+
fftw_complex *dest = ALLOC_N( fftw_complex, size );
|
75
|
+
rbDest = Data_Wrap_Struct( rb_const_get( mModule, rb_intern( "Malloc" ) ), 0, xfree,
|
76
|
+
(void *)dest );
|
77
|
+
rb_ivar_set( rbDest, rb_intern( "@size" ), INT2NUM( size * sizeof( fftw_complex ) ) );
|
78
|
+
fftw_plan plan =
|
79
|
+
fftw_plan_dft( rank, n.get(), source, dest, sign,
|
80
|
+
FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
|
81
|
+
ERRORMACRO( plan != NULL, Error, , "Error creating FFTW plan" );
|
82
|
+
fftw_execute( plan );
|
83
|
+
fftw_destroy_plan( plan );
|
84
|
+
} else {
|
85
|
+
ERRORMACRO( false, Error, ,
|
86
|
+
"This datatype is not supported for the selected transform" );
|
87
|
+
};
|
88
|
+
rbRetVal = rb_funcall2( rb_funcall( rb_const_get( mModule, rb_intern( "Sequence" ) ),
|
89
|
+
rb_intern( "import" ), 3,
|
90
|
+
rbTypecode, rbDest, rbSize ),
|
91
|
+
rb_intern( "reshape" ), rank, RARRAY_PTR(rbShape) );
|
92
|
+
} catch ( std::exception &e ) {
|
93
|
+
rb_raise( rb_eRuntimeError, "%s", e.what() );
|
94
|
+
};
|
95
|
+
return rbRetVal;
|
96
|
+
}
|
97
|
+
|
98
|
+
VALUE Node::wrapRFFT( VALUE rbSelf, VALUE rbForward )
|
99
|
+
{
|
100
|
+
VALUE rbRetVal = Qnil;
|
101
|
+
try {
|
102
|
+
int sign = rbForward == Qtrue ? FFTW_FORWARD : FFTW_BACKWARD;
|
103
|
+
VALUE rbMalloc = rb_funcall( rbSelf, rb_intern( "memory" ), 0 );
|
104
|
+
VALUE rbTypecode = rb_funcall( rbSelf, rb_intern( "typecode" ), 0 );
|
105
|
+
VALUE rbInSize = rb_funcall( rbSelf, rb_intern( "size" ), 0 );
|
106
|
+
int inSize = NUM2INT( rbInSize );
|
107
|
+
VALUE rbShape = rb_funcall( rbSelf, rb_intern( "shape" ), 0 );
|
108
|
+
int rank = RARRAY_LEN( rbShape );
|
109
|
+
shared_array< int > n( new int[ rank ] );
|
110
|
+
for ( int i=0; i<rank; i++ )
|
111
|
+
n[ rank - 1 - i ] = NUM2INT( RARRAY_PTR( rbShape )[i] );
|
112
|
+
VALUE rbDest = Qnil;
|
113
|
+
VALUE rbRetType = Qnil;
|
114
|
+
int size = 0;
|
115
|
+
if ( rank > 0 ) {
|
116
|
+
if ( sign == FFTW_FORWARD ) {
|
117
|
+
ERRORMACRO( n[ rank - 1 ] % 2 == 0, Error, ,
|
118
|
+
"First dimension of array must be even for discrete "
|
119
|
+
"fourier transform of real data" );
|
120
|
+
int half = n[ rank - 1 ] / 2 + 1;
|
121
|
+
size = inSize / n[ rank - 1 ] * half;
|
122
|
+
RARRAY_PTR(rbShape)[0] = INT2NUM(half);
|
123
|
+
} else {
|
124
|
+
int twice = ( n[ rank - 1 ] - 1 ) * 2;
|
125
|
+
size = inSize / n[ rank - 1 ] * twice;
|
126
|
+
RARRAY_PTR(rbShape)[0] = INT2NUM(twice);
|
127
|
+
n[ rank - 1 ] = twice;
|
128
|
+
};
|
129
|
+
};
|
130
|
+
if ( ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
|
131
|
+
rb_const_get( mModule,
|
132
|
+
rb_intern( "SFLOAT" ) ) ) ==
|
133
|
+
Qtrue &&
|
134
|
+
sign == FFTW_FORWARD ) ||
|
135
|
+
( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
|
136
|
+
rb_const_get( mModule,
|
137
|
+
rb_intern( "SCOMPLEX" ) ) ) ==
|
138
|
+
Qtrue &&
|
139
|
+
sign == FFTW_BACKWARD ) ) {
|
140
|
+
assert( sizeof( fftwf_complex ) == sizeof( float ) * 2 );
|
141
|
+
fftwf_complex *source;
|
142
|
+
Data_Get_Struct( rbMalloc, fftwf_complex, source );
|
143
|
+
void *dest;
|
144
|
+
fftwf_plan plan;
|
145
|
+
shared_array< fftwf_complex > copy;
|
146
|
+
int retTypeSize;
|
147
|
+
if ( sign == FFTW_FORWARD ) {
|
148
|
+
dest = ALLOC_N( fftwf_complex, size );
|
149
|
+
retTypeSize = sizeof( fftwf_complex );
|
150
|
+
plan = fftwf_plan_dft_r2c( rank, n.get(), (float *)source,
|
151
|
+
(fftwf_complex *)dest,
|
152
|
+
FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
|
153
|
+
rbRetType = rb_const_get( mModule, rb_intern( "SCOMPLEX" ) );
|
154
|
+
} else {
|
155
|
+
dest = ALLOC_N( float, size );
|
156
|
+
retTypeSize = sizeof( float );
|
157
|
+
// FFTW_PRESERVE_INPUT not supported in this case.
|
158
|
+
copy = shared_array< fftwf_complex >( new fftwf_complex[ inSize ] );
|
159
|
+
memcpy( copy.get(), source, inSize * sizeof( fftwf_complex ) );
|
160
|
+
plan = fftwf_plan_dft_c2r( rank, n.get(), (fftwf_complex *)copy.get(),
|
161
|
+
(float *)dest, FFTW_ESTIMATE );
|
162
|
+
rbRetType = rb_const_get( mModule, rb_intern( "SFLOAT" ) );
|
163
|
+
};
|
164
|
+
rbDest = Data_Wrap_Struct( rb_const_get( mModule, rb_intern( "Malloc" ) ), 0, xfree,
|
165
|
+
(void *)dest );
|
166
|
+
rb_ivar_set( rbDest, rb_intern( "@size" ), INT2NUM( size * retTypeSize ) );
|
167
|
+
ERRORMACRO( plan != NULL, Error, , "Error creating FFTW plan" );
|
168
|
+
fftwf_execute( plan );
|
169
|
+
fftwf_destroy_plan( plan );
|
170
|
+
} else if ( ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
|
171
|
+
rb_const_get( mModule,
|
172
|
+
rb_intern( "DFLOAT" ) ) ) ==
|
173
|
+
Qtrue &&
|
174
|
+
sign == FFTW_FORWARD ) ||
|
175
|
+
( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
|
176
|
+
rb_const_get( mModule,
|
177
|
+
rb_intern( "DCOMPLEX" ) ) ) ==
|
178
|
+
Qtrue &&
|
179
|
+
sign == FFTW_BACKWARD ) ) {
|
180
|
+
assert( sizeof( fftw_complex ) == sizeof( double ) * 2 );
|
181
|
+
fftw_complex *source;
|
182
|
+
Data_Get_Struct( rbMalloc, fftw_complex, source );
|
183
|
+
void *dest;
|
184
|
+
fftw_plan plan;
|
185
|
+
shared_array< fftw_complex > copy;
|
186
|
+
int retTypeSize;
|
187
|
+
if ( sign == FFTW_FORWARD ) {
|
188
|
+
dest = ALLOC_N( fftw_complex, size );
|
189
|
+
retTypeSize = sizeof( fftw_complex );
|
190
|
+
plan = fftw_plan_dft_r2c( rank, n.get(), (double *)source,
|
191
|
+
(fftw_complex *)dest,
|
192
|
+
FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
|
193
|
+
rbRetType = rb_const_get( mModule, rb_intern( "DCOMPLEX" ) );
|
194
|
+
} else {
|
195
|
+
dest = ALLOC_N( double, size );
|
196
|
+
retTypeSize = sizeof( double );
|
197
|
+
// FFTW_PRESERVE_INPUT not supported in this case.
|
198
|
+
copy = shared_array< fftw_complex >( new fftw_complex[ inSize ] );
|
199
|
+
memcpy( copy.get(), source, inSize * sizeof( fftw_complex ) );
|
200
|
+
plan = fftw_plan_dft_c2r( rank, n.get(), (fftw_complex *)copy.get(),
|
201
|
+
(double *)dest, FFTW_ESTIMATE );
|
202
|
+
rbRetType = rb_const_get( mModule, rb_intern( "DFLOAT" ) );
|
203
|
+
};
|
204
|
+
rbDest = Data_Wrap_Struct( rb_const_get( mModule, rb_intern( "Malloc" ) ), 0, xfree,
|
205
|
+
(void *)dest );
|
206
|
+
rb_ivar_set( rbDest, rb_intern( "@size" ), INT2NUM( size * retTypeSize ) );
|
207
|
+
ERRORMACRO( plan != NULL, Error, , "Error creating FFTW plan" );
|
208
|
+
fftw_execute( plan );
|
209
|
+
fftw_destroy_plan( plan );
|
210
|
+
} else {
|
211
|
+
ERRORMACRO( false, Error, ,
|
212
|
+
"This datatype is not supported for the selected transform" );
|
213
|
+
};
|
214
|
+
rbRetVal = rb_funcall2( rb_funcall( rb_const_get( mModule, rb_intern( "Sequence" ) ),
|
215
|
+
rb_intern( "import" ), 3,
|
216
|
+
rbRetType, rbDest, INT2NUM( size ) ),
|
217
|
+
rb_intern( "reshape" ), rank, RARRAY_PTR(rbShape) );
|
218
|
+
} catch ( std::exception &e ) {
|
219
|
+
rb_raise( rb_eRuntimeError, "%s", e.what() );
|
220
|
+
};
|
221
|
+
return rbRetVal;
|
222
|
+
}
|
223
|
+
|