beeps 0.1.32 → 0.1.33

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/beeps/adsr.cpp +139 -0
  3. data/.doc/ext/beeps/analyser.cpp +128 -0
  4. data/.doc/ext/beeps/beeps.cpp +9 -1
  5. data/.doc/ext/beeps/file_in.cpp +55 -9
  6. data/.doc/ext/beeps/gain.cpp +64 -0
  7. data/.doc/ext/beeps/mic_in.cpp +83 -0
  8. data/.doc/ext/beeps/native.cpp +20 -8
  9. data/.doc/ext/beeps/oscillator.cpp +88 -0
  10. data/.doc/ext/beeps/pitch_shift.cpp +64 -0
  11. data/.doc/ext/beeps/processor.cpp +31 -2
  12. data/.doc/ext/beeps/sound.cpp +90 -7
  13. data/.doc/ext/beeps/sound_player.cpp +156 -0
  14. data/.doc/ext/beeps/time_stretch.cpp +64 -0
  15. data/.github/workflows/release-gem.yml +4 -1
  16. data/ChangeLog.md +8 -0
  17. data/Rakefile +26 -4
  18. data/VERSION +1 -1
  19. data/beeps.gemspec +2 -2
  20. data/ext/beeps/adsr.cpp +150 -0
  21. data/ext/beeps/analyser.cpp +134 -0
  22. data/ext/beeps/beeps.cpp +10 -1
  23. data/ext/beeps/extconf.rb +1 -2
  24. data/ext/beeps/file_in.cpp +60 -9
  25. data/ext/beeps/gain.cpp +67 -0
  26. data/ext/beeps/mic_in.cpp +88 -0
  27. data/ext/beeps/native.cpp +20 -8
  28. data/ext/beeps/oscillator.cpp +93 -0
  29. data/ext/beeps/pitch_shift.cpp +67 -0
  30. data/ext/beeps/processor.cpp +34 -2
  31. data/ext/beeps/sound.cpp +99 -7
  32. data/ext/beeps/sound_player.cpp +169 -0
  33. data/ext/beeps/time_stretch.cpp +67 -0
  34. data/include/beeps/beeps.h +2 -1
  35. data/include/beeps/filter.h +179 -0
  36. data/include/beeps/generator.h +120 -0
  37. data/include/beeps/processor.h +37 -68
  38. data/include/beeps/ruby/filter.h +78 -0
  39. data/include/beeps/ruby/generator.h +60 -0
  40. data/include/beeps/ruby/processor.h +5 -45
  41. data/include/beeps/ruby/sound.h +14 -3
  42. data/include/beeps/signals.h +10 -4
  43. data/include/beeps/sound.h +67 -2
  44. data/lib/beeps/beeps.rb +6 -1
  45. data/lib/beeps/processor.rb +95 -15
  46. data/lib/beeps/sound.rb +29 -2
  47. data/src/adsr.cpp +245 -0
  48. data/src/analyser.cpp +254 -0
  49. data/src/beeps.cpp +11 -2
  50. data/src/file_in.cpp +94 -0
  51. data/src/gain.cpp +55 -0
  52. data/src/mic_in.cpp +262 -0
  53. data/src/mic_in.h +20 -0
  54. data/src/openal.cpp +2 -1
  55. data/src/oscillator.cpp +145 -0
  56. data/src/osx/signals.mm +83 -0
  57. data/src/pitch_shift.cpp +82 -0
  58. data/src/processor.cpp +202 -88
  59. data/src/processor.h +98 -0
  60. data/src/signals.cpp +326 -20
  61. data/src/signals.h +192 -2
  62. data/src/sound.cpp +735 -113
  63. data/src/sound.h +6 -1
  64. data/src/time_stretch.cpp +91 -0
  65. data/test/helper.rb +2 -1
  66. data/test/test_beeps.rb +10 -7
  67. data/test/test_beeps_init.rb +18 -0
  68. data/test/test_file_in.rb +15 -0
  69. data/test/test_processor.rb +50 -0
  70. data/test/test_sound.rb +87 -11
  71. data/test/test_sound_player.rb +134 -0
  72. metadata +54 -16
  73. data/.doc/ext/beeps/sawtooth_wave.cpp +0 -61
  74. data/.doc/ext/beeps/sine_wave.cpp +0 -61
  75. data/.doc/ext/beeps/square_wave.cpp +0 -61
  76. data/ext/beeps/sawtooth_wave.cpp +0 -64
  77. data/ext/beeps/sine_wave.cpp +0 -64
  78. data/ext/beeps/square_wave.cpp +0 -64
data/src/signals.cpp CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
 
4
4
  #include <memory>
5
+ #include <algorithm>
6
+ #include <AudioFile.h>
7
+ #include <CDSPResampler.h>
5
8
  #include "beeps/beeps.h"
6
9
  #include "beeps/exception.h"
7
10
 
@@ -13,12 +16,14 @@ namespace Beeps
13
16
  struct Signals::Data
14
17
  {
15
18
 
16
- std::unique_ptr<stk::StkFrames> frames;
19
+ std::unique_ptr<Frames> frames;
20
+
21
+ uint nsamples = 0;
17
22
 
18
23
  };// Signals::Data
19
24
 
20
25
 
21
- stk::StkFrames*
26
+ Frames*
22
27
  Signals_get_frames (Signals* signals)
23
28
  {
24
29
  if (!signals)
@@ -27,19 +32,304 @@ namespace Beeps
27
32
  return signals->self->frames.get();
28
33
  }
29
34
 
30
- const stk::StkFrames*
35
+ const Frames*
31
36
  Signals_get_frames (const Signals* signals)
32
37
  {
33
38
  return Signals_get_frames(const_cast<Signals*>(signals));
34
39
  }
35
40
 
41
+ Signals
42
+ Signals_create (uint capacity, uint nchannels, double sample_rate)
43
+ {
44
+ if (capacity <= 0 || nchannels <= 0)
45
+ argument_error(__FILE__, __LINE__);
46
+
47
+ if (sample_rate <= 0) sample_rate = Beeps::sample_rate();
48
+
49
+ Signals s;
50
+ s.self->frames.reset(new Frames(capacity, nchannels));
51
+ s.self->frames->setDataRate(sample_rate);
52
+ return s;
53
+ }
54
+
55
+ Signals
56
+ Signals_create (
57
+ const float* const* channels,
58
+ uint nsamples, uint nchannels, double sample_rate)
59
+ {
60
+ if (!channels || nsamples <= 0 || nchannels <= 0)
61
+ argument_error(__FILE__, __LINE__);
62
+
63
+ if (sample_rate <= 0) sample_rate = Beeps::sample_rate();
64
+
65
+ Frames* frames = new Frames(nsamples, nchannels);
66
+ frames->setDataRate(sample_rate);
67
+
68
+ for (uint channel = 0; channel < nchannels; ++channel)
69
+ for (uint sample = 0; sample < nsamples; ++sample)
70
+ (*frames)(sample, channel) = channels[channel][sample];
71
+
72
+ Signals s;
73
+ s.self->frames.reset(frames);
74
+ s.self->nsamples = nsamples;
75
+ return s;
76
+ }
77
+
78
+ void
79
+ Signals_resize (Signals* signals, uint capacity)
80
+ {
81
+ if (!signals || !*signals || capacity <= 0)
82
+ argument_error(__FILE__, __LINE__);
83
+
84
+ signals->self->frames->resize(capacity, signals->nchannels());
85
+ Signals_clear(signals);
86
+ }
87
+
88
+ void
89
+ Signals_resize (Signals* signals, uint nsamples, Float value)
90
+ {
91
+ Signals_resize(signals, nsamples);
92
+
93
+ Frames* frames = Signals_get_frames(signals);
94
+ assert(frames);
95
+
96
+ Float* p = &(*frames)(0, 0);
97
+ size_t size = frames->size();
98
+ for (size_t i = 0; i < size; ++i)
99
+ *p++ = value;
100
+
101
+ Signals_set_nsamples(signals, nsamples);
102
+ }
103
+
104
+ void
105
+ Signals_clear (Signals* signals)
106
+ {
107
+ if (!signals)
108
+ argument_error(__FILE__, __LINE__);
109
+
110
+ signals->self->nsamples = 0;
111
+ }
112
+
113
+ static uint
114
+ copy_frames (Signals* to, const Signals& from, uint from_offset)
115
+ {
116
+ assert(to && *to && from);
117
+
118
+ if (from_offset >= from.nsamples())
119
+ return 0;
120
+
121
+ uint to_offset = to->nsamples();
122
+ uint to_nsamples = to->capacity() - to_offset;
123
+ uint from_nsamples = from.nsamples();
124
+ uint copy_nsamples =
125
+ std::min(from_offset + to_nsamples, from_nsamples)
126
+ - from_offset;
127
+
128
+ Frames* to_frames = Signals_get_frames(to);
129
+ const Frames* from_frames = Signals_get_frames(&from);
130
+ assert(to_frames && from_frames);
131
+
132
+ for (uint channel = 0; channel < to_frames->nchannels(); ++channel)
133
+ {
134
+ uint from_channel = channel < from_frames->nchannels() ? channel : 0;
135
+
136
+ for (uint i = 0; i < copy_nsamples; ++i)
137
+ {
138
+ (*to_frames)( to_offset + i, channel) =
139
+ (*from_frames)(from_offset + i, from_channel);
140
+ }
141
+ }
142
+
143
+ to->self->nsamples += copy_nsamples;
144
+ return copy_nsamples;
145
+ }
146
+
147
+ static uint
148
+ resample_frames (Signals* to, const Signals& from, uint from_offset)
149
+ {
150
+ assert(to && *to && from);
151
+
152
+ if (from_offset >= from.nsamples())
153
+ return 0;
36
154
 
37
- Signals::Signals (float seconds, uint channels)
155
+ uint to_offset = to->nsamples();
156
+ float to_sec = (to->capacity() - to_offset) / to->sample_rate();
157
+ float from_offset_sec = from_offset / from.sample_rate();
158
+ float from_sec = from.nsamples() / from.sample_rate();
159
+
160
+ float copy_seconds =
161
+ std::min(from_offset_sec + to_sec, from_sec) - from_offset_sec;
162
+
163
+ uint to_nsamples = 0, from_nsamples = 0;
164
+ if (from_offset_sec + to_sec <= from_sec)
165
+ {
166
+ to_nsamples = to->capacity() - to->nsamples();
167
+ from_nsamples = copy_seconds * from.sample_rate();
168
+ }
169
+ else
170
+ {
171
+ to_nsamples = copy_seconds * to->sample_rate();
172
+ from_nsamples = from.nsamples() - from_offset;
173
+ }
174
+
175
+ r8b::CDSPResampler24 resampler(
176
+ from.sample_rate(), to->sample_rate(), from_nsamples);
177
+ r8b::CFixedBuffer<double> from_buf(from_nsamples), to_buf(to_nsamples);
178
+
179
+ Frames* to_frames = Signals_get_frames(to);
180
+ const Frames* from_frames = Signals_get_frames(&from);
181
+ assert(to_frames && from_frames);
182
+
183
+ for (uint channel = 0; channel < to_frames->nchannels(); ++channel)
184
+ {
185
+ uint from_channel = channel < from_frames->nchannels() ? channel : 0;
186
+
187
+ for (uint i = 0; i < from_nsamples; ++i)
188
+ from_buf[i] = (*from_frames)(from_offset + i, from_channel);
189
+
190
+ resampler.clear();
191
+ resampler.oneshot(
192
+ (const double*) from_buf, from_nsamples,
193
+ (double*) to_buf, to_nsamples);
194
+
195
+ for (uint i = 0; i < to_nsamples; ++i)
196
+ (*to_frames)(to_offset + i, channel) = to_buf[i];
197
+ }
198
+
199
+ to->self->nsamples += to_nsamples;
200
+ return from_nsamples;
201
+ }
202
+
203
+ uint
204
+ Signals_copy (Signals* to, const Signals& from, uint from_offset)
38
205
  {
39
- if (seconds <= 0 || channels <= 0)
40
- return;
206
+ if (!to || !*to || !from)
207
+ argument_error(__FILE__, __LINE__);
208
+
209
+ Signals_clear(to);
41
210
 
42
- self->frames.reset(new stk::StkFrames(seconds * sampling_rate(), channels));
211
+ if (to->sample_rate() == from.sample_rate())
212
+ return copy_frames(to, from, from_offset);
213
+ else
214
+ return resample_frames(to, from, from_offset);
215
+ }
216
+
217
+ void
218
+ Signals_apply (Signals* signals, const Signals& multiplier)
219
+ {
220
+ if (!signals || multiplier.nchannels() != 1)
221
+ argument_error(__FILE__, __LINE__);
222
+
223
+ if (signals->capacity() != multiplier.capacity())
224
+ argument_error(__FILE__, __LINE__);
225
+
226
+ Frames* sigf = Signals_get_frames(signals);
227
+ Frames* mulf = Signals_get_frames(const_cast<Signals*>(&multiplier));
228
+
229
+ for (uint ch = 0; ch < sigf->nchannels(); ++ch)
230
+ {
231
+ Float* sigp = &(*sigf)(0, ch);
232
+ Float* mulp = &(*mulf)(0, 0);
233
+ uint nframes = sigf->nframes();
234
+ uint stride = sigf->nchannels();
235
+ for (uint i = 0; i < nframes; ++i, sigp += stride, ++mulp)
236
+ *sigp *= *mulp;
237
+ }
238
+ }
239
+
240
+ template <typename T>
241
+ void
242
+ write_samples (Signals* signals, const SignalSamples<T>& samples, long nsamples_)
243
+ {
244
+ uint nsamples = nsamples_ < 0 ? samples.nsamples() : (uint) nsamples_;
245
+
246
+ if (
247
+ !signals || !*signals ||
248
+ signals->nchannels() != samples.nchannels() ||
249
+ signals->capacity() < nsamples)
250
+ {
251
+ argument_error(__FILE__, __LINE__);
252
+ }
253
+
254
+ Frames* f = Signals_get_frames(signals);
255
+ assert(f);
256
+
257
+ for (uint channel = 0; channel < signals->nchannels(); ++channel)
258
+ {
259
+ const T* buf = samples.channel(channel);
260
+ for (uint sample = 0; sample < nsamples; ++sample)
261
+ (*f)(sample, channel) = buf[sample];
262
+ }
263
+
264
+ signals->self->nsamples = nsamples;
265
+ }
266
+
267
+ template <>
268
+ void
269
+ Signals_write_samples (
270
+ Signals* signals, const SignalSamples<float>& samples, long nsamples)
271
+ {
272
+ write_samples(signals, samples, nsamples);
273
+ }
274
+
275
+ template <>
276
+ void
277
+ Signals_write_samples (
278
+ Signals* signals, const SignalSamples<double>& samples, long nsamples)
279
+ {
280
+ write_samples(signals, samples, nsamples);
281
+ }
282
+
283
+ void
284
+ Signals_set_nsamples (Signals* signals, uint nsamples)
285
+ {
286
+ if (!signals)
287
+ argument_error(__FILE__, __LINE__);
288
+
289
+ signals->self->nsamples = nsamples;
290
+ }
291
+
292
+ float
293
+ Signals_get_seconds (const Signals& signals)
294
+ {
295
+ return (float) (signals.nsamples() / signals.sample_rate());
296
+ }
297
+
298
+ static void
299
+ make_audio_buffer (
300
+ AudioFile<float>::AudioBuffer* buffer, const Signals& signals)
301
+ {
302
+ assert(buffer && signals);
303
+
304
+ const Frames* frames = Signals_get_frames(&signals);
305
+ assert(frames);
306
+
307
+ buffer->clear();
308
+ for (uint ch = 0; ch < frames->nchannels(); ++ch)
309
+ {
310
+ buffer->emplace_back(std::vector<float>());
311
+ auto& channel = buffer->back();
312
+
313
+ uint nframes = frames->nframes();
314
+ channel.reserve(nframes);
315
+
316
+ for (uint i = 0; i < nframes; ++i)
317
+ channel.emplace_back((*frames)(i, ch));
318
+ }
319
+ }
320
+
321
+ void
322
+ Signals_save (const Signals& signals, const char* path)
323
+ {
324
+ AudioFile<float> file;
325
+ file.setSampleRate(signals.sample_rate());
326
+ make_audio_buffer(&file.samples, signals);
327
+ file.save(path);
328
+ }
329
+
330
+
331
+ Signals::Signals ()
332
+ {
43
333
  }
44
334
 
45
335
  Signals::~Signals ()
@@ -51,35 +341,51 @@ namespace Beeps
51
341
  {
52
342
  Signals t;
53
343
  if (self->frames)
54
- t.self->frames.reset(new stk::StkFrames(*self->frames));
344
+ t.self->frames.reset(new Frames(*self->frames));
345
+ t.self->nsamples = self->nsamples;
55
346
  return t;
56
347
  }
57
348
 
58
- float
59
- Signals::seconds () const
349
+ double
350
+ Signals::sample_rate () const
60
351
  {
61
- double sec = samples() / (double) sampling_rate();
62
- return (float) sec;
352
+ return self->frames ? self->frames->dataRate() : Beeps::sample_rate();
63
353
  }
64
354
 
65
355
  uint
66
- Signals::samples () const
356
+ Signals::nchannels () const
67
357
  {
68
- Data* p = self.get();
69
- return p ? p->frames->frames() : 0;
358
+ return self->frames ? self->frames->nchannels() : 0;
70
359
  }
71
360
 
72
361
  uint
73
- Signals::channels () const
362
+ Signals::nsamples () const
363
+ {
364
+ return self->nsamples;
365
+ }
366
+
367
+ uint
368
+ Signals::capacity () const
369
+ {
370
+ return self->frames ? self->frames->nframes() : 0;
371
+ }
372
+
373
+ bool
374
+ Signals::empty () const
375
+ {
376
+ return self->nsamples == 0;
377
+ }
378
+
379
+ const double*
380
+ Signals::samples () const
74
381
  {
75
- Data* p = self.get();
76
- return p ? p->frames->channels() : 0;
382
+ return self->frames ? self->frames->data() : NULL;
77
383
  }
78
384
 
79
385
  Signals::operator bool () const
80
386
  {
81
- const stk::StkFrames* f = Signals_get_frames(this);
82
- return f && f->frames() > 0 && f->channels() > 0;
387
+ const Frames* f = self->frames.get();
388
+ return f && f->dataRate() > 0 && f->nchannels() > 0 && f->nframes() > 0;
83
389
  }
84
390
 
85
391
  bool
data/src/signals.h CHANGED
@@ -4,17 +4,207 @@
4
4
  #define __BEEPS_SRC_SIGNALS_H__
5
5
 
6
6
 
7
+ #include <vector>
7
8
  #include <Stk.h>
9
+ #include <xot/pimpl.h>
8
10
  #include <beeps/signals.h>
11
+ #include <beeps/exception.h>
9
12
 
10
13
 
11
14
  namespace Beeps
12
15
  {
13
16
 
14
17
 
15
- stk::StkFrames* Signals_get_frames ( Signals* signals);
18
+ typedef stk::StkFloat Float;
16
19
 
17
- const stk::StkFrames* Signals_get_frames (const Signals* signals);
20
+
21
+ class Frames;
22
+
23
+ template <typename T> class SignalSamples;
24
+
25
+
26
+ Frames* Signals_get_frames ( Signals* signals);
27
+
28
+ const Frames* Signals_get_frames (const Signals* signals);
29
+
30
+ Signals Signals_create (
31
+ uint capacity, uint nchannels = 1, double sample_rate = 0);
32
+
33
+ Signals Signals_create (
34
+ const float* const* channels,
35
+ uint nsamples, uint nchannels, double sample_rate = 0);
36
+
37
+ void Signals_resize (Signals* signals, uint capacity);
38
+
39
+ void Signals_resize (Signals* signals, uint nsamples, Float value);
40
+
41
+ void Signals_clear (Signals* signals);
42
+
43
+ uint Signals_copy (Signals* to, const Signals& from, uint from_offset);
44
+
45
+ void Signals_apply (Signals* signals, const Signals& multiplier);
46
+
47
+ template <typename T>
48
+ void Signals_write_samples (
49
+ Signals* signals, const SignalSamples<T>& samples, long nsamples = -1);
50
+
51
+ void Signals_set_nsamples (Signals* signals, uint nsamples);
52
+
53
+ float Signals_get_seconds (const Signals& signals);
54
+
55
+ void Signals_save (const Signals& signals, const char* path);
56
+
57
+ Signals Signals_load (const char* path);
58
+
59
+
60
+ class Frames : public stk::StkFrames
61
+ {
62
+
63
+ public:
64
+
65
+ Frames (unsigned int nFrames = 0, unsigned int nChannels = 0)
66
+ : stk::StkFrames(nFrames, nChannels)
67
+ {
68
+ }
69
+
70
+ size_t slice (size_t start, size_t length)
71
+ {
72
+ assert(!saved_data_);
73
+
74
+ size_t end = start + length;
75
+ assert(start <= nFrames_ && end <= nFrames_);
76
+
77
+ saved_data_ = data_;
78
+ saved_nFrames_ = nFrames_;
79
+ saved_size_ = size_;
80
+ saved_bufferSize_ = bufferSize_;
81
+
82
+ data_ += start * nChannels_;
83
+ nFrames_ = length;
84
+ size_ = length * nChannels_;
85
+ bufferSize_ = size_;
86
+
87
+ return end;
88
+ }
89
+
90
+ void unslice ()
91
+ {
92
+ assert(saved_data_);
93
+
94
+ data_ = saved_data_;
95
+ nFrames_ = saved_nFrames_;
96
+ size_ = saved_size_;
97
+ bufferSize_ = saved_bufferSize_;
98
+ }
99
+
100
+ uint nchannels () const
101
+ {
102
+ return nChannels_;
103
+ }
104
+
105
+ uint nframes () const
106
+ {
107
+ return nFrames_;
108
+ }
109
+
110
+ const Float* data () const
111
+ {
112
+ return data_;
113
+ }
114
+
115
+ private:
116
+
117
+ Float* saved_data_ = NULL;
118
+
119
+ size_t saved_nFrames_ = 0, saved_size_ = 0, saved_bufferSize_ = 0;
120
+
121
+ };// Frames
122
+
123
+
124
+ template <typename T>
125
+ class SignalSamples
126
+ {
127
+
128
+ typedef SignalSamples<T> This;
129
+
130
+ public:
131
+
132
+ SignalSamples (uint nsamples, uint nchannels)
133
+ {
134
+ self->nsamples = nsamples;
135
+ self->samples.resize(nsamples * nchannels, 0);
136
+ self->update_channels(nchannels);
137
+ }
138
+
139
+ SignalSamples (const Signals& signals)
140
+ {
141
+ if (!signals)
142
+ argument_error(__FILE__, __LINE__);
143
+
144
+ const Frames* f = Signals_get_frames(&signals);
145
+ assert(f);
146
+
147
+ uint nsamples = signals.nsamples();
148
+ uint nchannels = signals.nchannels();
149
+
150
+ auto& samples = self->samples;
151
+ samples.reserve(nsamples * nchannels);
152
+
153
+ for (uint channel = 0; channel < nchannels; ++channel)
154
+ {
155
+ for (uint sample = 0; sample < nsamples; ++sample)
156
+ samples.push_back((*f)(sample, channel));
157
+ }
158
+
159
+ self->nsamples = nsamples;
160
+ self->update_channels(nchannels);
161
+ }
162
+
163
+ uint nchannels () const
164
+ {
165
+ return (uint) self->channels.size();
166
+ }
167
+
168
+ uint nsamples () const
169
+ {
170
+ return self->nsamples;
171
+ }
172
+
173
+ T* const* channels () {return &self->channels[0];}
174
+
175
+ const T* const* channels () const
176
+ {
177
+ return const_cast<This*>(this)->channels();
178
+ }
179
+
180
+ T* channel (uint index) {return self->channels[index];}
181
+
182
+ const T* channel (uint index) const
183
+ {
184
+ return const_cast<This*>(this)->channel(index);
185
+ }
186
+
187
+ struct Data
188
+ {
189
+
190
+ uint nsamples = 0;
191
+
192
+ std::vector<T> samples;
193
+
194
+ std::vector<T*> channels;
195
+
196
+ void update_channels (uint nchannels)
197
+ {
198
+ channels.clear();
199
+ for (uint i = 0; i < nchannels; ++i)
200
+ channels.push_back(&samples[nsamples * i]);
201
+ }
202
+
203
+ };// Data
204
+
205
+ Xot::PSharedImpl<Data> self;
206
+
207
+ };// SignalSamples
18
208
 
19
209
 
20
210
  }// Beeps