rockbox_ffi 0.2.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78ae6470117f6b52f565285e04c0d65742da1296fc3b336ad6cf3c246266c5ee
4
- data.tar.gz: 11281048c1996c24ff83ec3ceb1f5d66c8529527f2f0f91e3498836eb4fc39ad
3
+ metadata.gz: 9e881324e0bb82a716a98417325a1ec9823d3a0f1f015444c6e2332f7b942c65
4
+ data.tar.gz: 70e45aa728fb5d2449e1849f993780a0dad201f32113eb4f352344df0bed1fb6
5
5
  SHA512:
6
- metadata.gz: dcc042306ae5c1ee6afe35a951a0f3ac2da9ad78b33605009b04f755ef23f253efb061f2ddb64afef34760b0a5642b1bc77ddbcfb95db8cc99b1d1198a41feb7
7
- data.tar.gz: 9bf8d8eacd74b7cdef21bfe8bd80f810c58ae742e752f9a12709bd9df296f8a9b502ab014b5335fec522800f6c232dedf6796f168c5a8e07a3ddbea2f36cdb96
6
+ metadata.gz: b76132c6b6db3f4eee20a990707b9e65e4a7a65cd490f1d690dbf0c8c72338a7c855cf75c12deb74171d6dd3833f2394dc33ad745e87172b0d1423e631e32f59
7
+ data.tar.gz: 7ef952e10d6a0728a4ef251ef43480ab6bc4d5d7fe3b7514b3c933f9cef99768ec129552f964cf3641ed98973e5831ab6272690f30b4c0fc6eca8a02d06d0f0e
data/README.md CHANGED
@@ -10,6 +10,10 @@ via [`fiddle`](https://docs.ruby-lang.org/en/master/Fiddle.html) (Ruby stdlib)
10
10
  over the prebuilt `librockbox_ffi` shared library. No native extension is
11
11
  compiled — the gem `dlopen`s the shared library at load time.
12
12
 
13
+ > 📖 **Sound settings reference** — the equalizer, tone, crossfeed, compressor
14
+ > and other DSP controls mirror Rockbox's own. See the official
15
+ > [Rockbox manual — Sound Settings](https://download.rockbox.org/daily/manual/rockbox-ipodvideo/rockbox-buildch6.html).
16
+
13
17
  ## Setup
14
18
 
15
19
  Build the shared library once (from the repo root):
data/examples/play.rb CHANGED
@@ -13,9 +13,16 @@ FIXTURE = File.join(REPO, "crates", "rocksky", "fixtures", "08 - Internet Money
13
13
  file = ARGV[0] || FIXTURE
14
14
 
15
15
  player = RockboxFFI::Player.new(volume: 0.8)
16
- player.set_queue([file])
17
- player.play
16
+ # Mutating setters return self, so the setup reads as one fluent chain.
17
+ # DSP: Bass Boost preset + a +7 dB bass / +4 dB treble lift.
18
+ player
19
+ .set_queue([file])
20
+ .set_eq_preset(RockboxFFI::EqPreset::BASS_BOOST)
21
+ .set_bass(7)
22
+ .set_treble(4)
23
+ .play
18
24
  puts "▶ playing #{file}"
25
+ puts "eq: BassBoost preset, bass +7 dB, treble +4 dB"
19
26
 
20
27
  # Reinstall a SIGINT handler AFTER the player boots: the native audio engine
21
28
  # installs its own signal handler while starting the output device, which
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module RockboxFFI
6
+ # Decode a local audio file to interleaved-stereo S16 PCM, one chunk at a
7
+ # time, using the Rockbox codec engine.
8
+ #
9
+ # The codec state is process-wide, so only one Decoder may decode at a time
10
+ # and it must be used from one thread. Call #close when done, or use the block
11
+ # form Decoder.open(path) { |dec| ... }.
12
+ class Decoder
13
+ # Open a Decoder for +path+; if a block is given, close it automatically
14
+ # afterwards.
15
+ def self.open(path)
16
+ dec = new(path)
17
+ return dec unless block_given?
18
+
19
+ begin
20
+ yield dec
21
+ ensure
22
+ dec.close
23
+ end
24
+ end
25
+
26
+ def initialize(path)
27
+ @ptr = Lib.rb_decoder_open(path.to_s)
28
+ raise "could not open decoder for #{path.inspect}" if @ptr.null?
29
+
30
+ @finalizer = self.class.send(:finalizer, @ptr)
31
+ ObjectSpace.define_finalizer(self, @finalizer)
32
+ end
33
+
34
+ # -- lifecycle --------------------------------------------------------
35
+ def close
36
+ return if @ptr.nil?
37
+
38
+ ObjectSpace.undefine_finalizer(self)
39
+ Lib.rb_decoder_free(@ptr)
40
+ @ptr = nil
41
+ end
42
+
43
+ def closed?
44
+ @ptr.nil?
45
+ end
46
+
47
+ def self.finalizer(ptr)
48
+ proc { Lib.rb_decoder_free(ptr) }
49
+ end
50
+ private_class_method :finalizer
51
+
52
+ # -- metadata ---------------------------------------------------------
53
+ # Tags and stream properties (same shape as Metadata.read). Returns a Hash
54
+ # with symbol keys. Raises on failure.
55
+ def metadata
56
+ s = RockboxFFI.take_string(Lib.rb_decoder_metadata_json(@ptr))
57
+ raise "rb_decoder_metadata_json returned NULL" if s.nil?
58
+
59
+ JSON.parse(s, symbolize_names: true)
60
+ end
61
+
62
+ # -- decoding ---------------------------------------------------------
63
+ # Next buffer of decoded PCM as +[samples, sample_rate]+, where +samples+
64
+ # is an Array of interleaved stereo int16 Integers. Returns nil at end of
65
+ # track (or after a codec error — see #finished).
66
+ def next_chunk
67
+ out_len = "\0" * Fiddle::SIZEOF_VOIDP # size_t* out-param (i16 count)
68
+ out_rate = "\0" * Fiddle::SIZEOF_INT # uint32_t* out-param (Hz)
69
+ ptr = Lib.rb_decoder_next_chunk(@ptr, out_len, out_rate)
70
+
71
+ produced = out_len.unpack1(Fiddle::SIZEOF_VOIDP == 8 ? "Q" : "L")
72
+ return nil if ptr.null? || produced.zero?
73
+
74
+ rate = out_rate.unpack1("L")
75
+ begin
76
+ ptr.size = produced * 2
77
+ [ptr[0, produced * 2].unpack("s<*"), rate]
78
+ ensure
79
+ Lib.rb_buffer_free(ptr, produced)
80
+ end
81
+ end
82
+
83
+ # Yield each +[samples, sample_rate]+ chunk until end of track. Returns an
84
+ # Enumerator when no block is given.
85
+ def each_chunk
86
+ return enum_for(:each_chunk) unless block_given?
87
+
88
+ while (chunk = next_chunk)
89
+ yield chunk
90
+ end
91
+ end
92
+
93
+ # Request a seek to +ms+ milliseconds.
94
+ def seek_ms(ms)
95
+ Lib.rb_decoder_seek_ms(@ptr, Integer(ms))
96
+ end
97
+
98
+ # Playback position last reported by the codec, in milliseconds.
99
+ def elapsed_ms
100
+ Lib.rb_decoder_elapsed_ms(@ptr)
101
+ end
102
+
103
+ # +[done, code]+: +done+ is true once the track has ended; +code+ is the
104
+ # exit status (0 = clean end, negative = codec error), valid only when
105
+ # +done+.
106
+ def finished
107
+ out_code = "\0" * Fiddle::SIZEOF_INT # int32_t* out-param
108
+ r = Lib.rb_decoder_finished(@ptr, out_code)
109
+ done = r == true || r == 1
110
+ [done, out_code.unpack1("l")]
111
+ end
112
+ end
113
+ end
@@ -18,6 +18,13 @@ module RockboxFFI
18
18
  ALBUM = 2
19
19
  end
20
20
 
21
+ # Values for Player#set_repeat / #repeat.
22
+ module RepeatMode
23
+ OFF = 0
24
+ ONE = 1
25
+ ALL = 2
26
+ end
27
+
21
28
  module CrossfadeMode
22
29
  OFF = 0
23
30
  AUTO_SKIP = 1
@@ -54,4 +61,47 @@ module RockboxFFI
54
61
  KARAOKE = 5
55
62
  SWAP = 6
56
63
  end
64
+
65
+ # Built-in EQ presets for Player#set_eq_preset.
66
+ module EqPreset
67
+ FLAT = 0
68
+ ACOUSTIC = 1
69
+ BASS_BOOST = 2
70
+ BASS_REDUCER = 3
71
+ CLASSICAL = 4
72
+ DANCE = 5
73
+ DEEP = 6
74
+ ELECTRONIC = 7
75
+ HIP_HOP = 8
76
+ JAZZ = 9
77
+ LATIN = 10
78
+ LOUDNESS = 11
79
+ LOUNGE = 12
80
+ PIANO = 13
81
+ POP = 14
82
+ RNB = 15
83
+ ROCK = 16
84
+ SMALL_SPEAKERS = 17
85
+ TREBLE_BOOST = 18
86
+ TREBLE_REDUCER = 19
87
+ VOCAL_BOOST = 20
88
+ end
89
+
90
+ # Crossfeed mode for Player#set_crossfeed.
91
+ module CrossfeedMode
92
+ OFF = 0
93
+ MEIER = 1
94
+ CUSTOM = 2
95
+ end
96
+
97
+ # Channel mode for Player#set_channel_mode.
98
+ module ChannelMode
99
+ STEREO = 0
100
+ MONO = 1
101
+ CUSTOM = 2
102
+ MONO_LEFT = 3
103
+ MONO_RIGHT = 4
104
+ KARAOKE = 5
105
+ SWAP = 6
106
+ end
57
107
  end
@@ -98,6 +98,15 @@ module RockboxFFI
98
98
  extern "void* rb_meta_read_json(void*)"
99
99
  extern "void* rb_meta_probe(void*)"
100
100
 
101
+ # ---- decoder ------------------------------------------------------
102
+ extern "void* rb_decoder_open(void*)"
103
+ extern "void rb_decoder_free(void*)"
104
+ extern "void* rb_decoder_metadata_json(void*)"
105
+ extern "int16_t* rb_decoder_next_chunk(void*, void*, void*)"
106
+ extern "void rb_decoder_seek_ms(void*, uint64_t)"
107
+ extern "uint64_t rb_decoder_elapsed_ms(void*)"
108
+ extern "bool rb_decoder_finished(void*, void*)"
109
+
101
110
  # ---- player -------------------------------------------------------
102
111
  extern "void* rb_player_new()"
103
112
  extern "void* rb_player_new_with_config(uint32_t, float, float, int32_t, float, bool, int32_t, uint32_t, uint32_t, uint32_t, uint32_t, int32_t)"
@@ -116,11 +125,39 @@ module RockboxFFI
116
125
  extern "void rb_player_skip_to(void*, size_t)"
117
126
  extern "void rb_player_seek_ms(void*, uint64_t)"
118
127
  extern "void rb_player_set_volume(void*, float)"
128
+ extern "void rb_player_set_balance(void*, int32_t)"
119
129
  extern "void rb_player_set_crossfade(void*, int32_t, uint32_t, uint32_t, uint32_t, uint32_t, int32_t)"
120
130
  extern "void rb_player_set_replaygain(void*, int32_t, float, bool)"
121
131
  extern "float rb_player_volume(void*)"
132
+ extern "int32_t rb_player_balance(void*)"
122
133
  extern "uint32_t rb_player_sample_rate(void*)"
123
134
  extern "void* rb_player_status_json(void*)"
135
+ extern "void rb_player_set_shuffle(void*, bool)"
136
+ extern "bool rb_player_is_shuffle_enabled(void*)"
137
+ extern "void rb_player_set_repeat(void*, int32_t)"
138
+ extern "int32_t rb_player_repeat(void*)"
139
+
140
+ # ---- player DSP ---------------------------------------------------
141
+ extern "void rb_player_set_eq_enabled(void*, bool)"
142
+ extern "bool rb_player_is_eq_enabled(void*)"
143
+ extern "void rb_player_set_eq_band(void*, size_t, int32_t, float, float)"
144
+ extern "void rb_player_set_eq_precut(void*, float)"
145
+ extern "void rb_player_set_eq_preset(void*, int32_t)"
146
+ extern "void rb_player_set_tone(void*, int32_t, int32_t, int32_t, int32_t)"
147
+ extern "void rb_player_set_bass(void*, int32_t)"
148
+ extern "void rb_player_set_treble(void*, int32_t)"
149
+ extern "void rb_player_set_bass_cutoff(void*, int32_t)"
150
+ extern "void rb_player_set_treble_cutoff(void*, int32_t)"
151
+ extern "void rb_player_set_crossfeed(void*, int32_t, int32_t, int32_t, int32_t, int32_t)"
152
+ extern "void rb_player_set_bass_enhancement(void*, int32_t, int32_t)"
153
+ extern "void rb_player_set_fatigue_reduction(void*, int32_t)"
154
+ extern "void rb_player_set_surround(void*, int32_t, int32_t, int32_t, int32_t)"
155
+ extern "void rb_player_set_channel_mode(void*, int32_t)"
156
+ extern "void rb_player_set_stereo_width(void*, int32_t)"
157
+ extern "void rb_player_set_compressor(void*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t)"
158
+ extern "void rb_player_set_dither(void*, bool)"
159
+ extern "void rb_player_set_pitch(void*, int32_t)"
160
+ extern "void* rb_player_dsp_settings_json(void*)"
124
161
 
125
162
  # ---- resume -------------------------------------------------------
126
163
  extern "void* rb_player_resume(void*)"
@@ -11,6 +11,11 @@ module RockboxFFI
11
11
  #
12
12
  # ReplayGain +mode+ here uses the *player* values: 0 off, 1 track, 2 album
13
13
  # (see ReplayGainMode) — distinct from the DSP encoding.
14
+ #
15
+ # Mutating setters (queue, transport, settings, DSP) return +self+ so calls
16
+ # can be fluently chained, e.g.
17
+ # player.set_queue([file]).set_shuffle(true).play
18
+ # Getters/queries and lifecycle methods keep their own return values.
14
19
  class Player
15
20
  DEFAULT_CONFIG = {
16
21
  sample_rate: 0, # 0 => device default
@@ -85,12 +90,18 @@ module RockboxFFI
85
90
  private_class_method :finalizer
86
91
 
87
92
  # -- queue ------------------------------------------------------------
93
+ # Replace the queue. Each entry may be a local file path, an http(s)://
94
+ # URL to a finite remote file, or a live-radio / streaming URL.
88
95
  def set_queue(paths)
89
96
  Lib.rb_player_set_queue_json(@ptr, JSON.generate(Array(paths).map(&:to_s)))
97
+ self
90
98
  end
91
99
 
100
+ # Append one track to the queue. +path+ may be a local file path, an
101
+ # http(s):// URL to a finite remote file, or a live-radio / streaming URL.
92
102
  def enqueue(path)
93
103
  Lib.rb_player_enqueue(@ptr, path.to_s)
104
+ self
94
105
  end
95
106
 
96
107
  # Insert +paths+ (a path/URL or Array of them) into the queue at
@@ -100,6 +111,7 @@ module RockboxFFI
100
111
  Lib.rb_player_insert_json(
101
112
  @ptr, JSON.generate(Array(paths).map(&:to_s)), Integer(position), Integer(index)
102
113
  )
114
+ self
103
115
  end
104
116
 
105
117
  # The current queue as an Array of String paths/URLs.
@@ -113,45 +125,65 @@ module RockboxFFI
113
125
  # -- transport --------------------------------------------------------
114
126
  def play
115
127
  Lib.rb_player_play(@ptr)
128
+ self
116
129
  end
117
130
 
118
131
  def pause
119
132
  Lib.rb_player_pause(@ptr)
133
+ self
120
134
  end
121
135
 
122
136
  def toggle
123
137
  Lib.rb_player_toggle(@ptr)
138
+ self
124
139
  end
125
140
 
126
141
  def stop
127
142
  Lib.rb_player_stop(@ptr)
143
+ self
128
144
  end
129
145
 
130
146
  def next
131
147
  Lib.rb_player_next(@ptr)
148
+ self
132
149
  end
133
150
 
134
151
  def previous
135
152
  Lib.rb_player_previous(@ptr)
153
+ self
136
154
  end
137
155
 
138
156
  def skip_to(index)
139
157
  Lib.rb_player_skip_to(@ptr, Integer(index))
158
+ self
140
159
  end
141
160
 
142
161
  def seek_ms(ms)
143
162
  Lib.rb_player_seek_ms(@ptr, Integer(ms))
163
+ self
144
164
  end
145
165
 
146
166
  # -- settings ---------------------------------------------------------
147
167
  def set_volume(vol)
148
168
  Lib.rb_player_set_volume(@ptr, Float(vol))
169
+ self
170
+ end
171
+
172
+ # Stereo balance, -100 (full left) to +100 (full right); 0 = centre.
173
+ def set_balance(balance)
174
+ Lib.rb_player_set_balance(@ptr, Integer(balance))
175
+ self
149
176
  end
150
177
 
151
178
  def volume
152
179
  Lib.rb_player_volume(@ptr)
153
180
  end
154
181
 
182
+ # Current stereo balance, -100 (full left) to +100 (full right).
183
+ def balance
184
+ Lib.rb_player_balance(@ptr)
185
+ end
186
+
155
187
  def sample_rate
156
188
  Lib.rb_player_sample_rate(@ptr)
157
189
  end
@@ -163,11 +195,173 @@ module RockboxFFI
163
195
  @ptr, Integer(mode), Integer(fade_out_delay_ms), Integer(fade_out_duration_ms),
164
196
  Integer(fade_in_delay_ms), Integer(fade_in_duration_ms), Integer(mix_mode)
165
197
  )
198
+ self
166
199
  end
167
200
 
168
201
  # mode: see ReplayGainMode (OFF=0, TRACK=1, ALBUM=2).
169
202
  def set_replaygain(mode, preamp_db, prevent_clipping)
170
203
  Lib.rb_player_set_replaygain(@ptr, Integer(mode), Float(preamp_db), RockboxFFI.b(prevent_clipping))
204
+ self
205
+ end
206
+
207
+ # Enable/disable shuffle.
208
+ def set_shuffle(enabled)
209
+ Lib.rb_player_set_shuffle(@ptr, RockboxFFI.b(enabled))
210
+ self
211
+ end
212
+
213
+ # Whether shuffle is currently enabled.
214
+ def shuffle_enabled?
215
+ !Lib.rb_player_is_shuffle_enabled(@ptr).zero?
216
+ end
217
+
218
+ # Set the repeat mode (see RepeatMode: OFF=0, ONE=1, ALL=2).
219
+ def set_repeat(mode)
220
+ Lib.rb_player_set_repeat(@ptr, Integer(mode))
221
+ self
222
+ end
223
+
224
+ # The current repeat mode as an Integer (see RepeatMode).
225
+ def repeat
226
+ Lib.rb_player_repeat(@ptr)
227
+ end
228
+
229
+ # -- DSP --------------------------------------------------------------
230
+ # Enable/disable the parametric equalizer.
231
+ def set_eq_enabled(enabled)
232
+ Lib.rb_player_set_eq_enabled(@ptr, RockboxFFI.b(enabled))
233
+ self
234
+ end
235
+
236
+ # Whether the parametric equalizer is currently enabled.
237
+ def eq_enabled?
238
+ !Lib.rb_player_is_eq_enabled(@ptr).zero?
239
+ end
240
+
241
+ # Configure one EQ band: +band+ index, +cutoff_hz+ center frequency,
242
+ # +q+ factor, +gain_db+ gain in dB.
243
+ def set_eq_band(band, cutoff_hz, q, gain_db)
244
+ Lib.rb_player_set_eq_band(@ptr, Integer(band), Integer(cutoff_hz), Float(q), Float(gain_db))
245
+ self
246
+ end
247
+
248
+ # Global EQ pre-cut in dB.
249
+ def set_eq_precut(db)
250
+ Lib.rb_player_set_eq_precut(@ptr, Float(db))
251
+ self
252
+ end
253
+
254
+ # Apply a built-in EQ preset (see EqPreset).
255
+ def set_eq_preset(preset)
256
+ Lib.rb_player_set_eq_preset(@ptr, Integer(preset))
257
+ self
258
+ end
259
+
260
+ # Bass/treble tone controls with explicit cutoff frequencies.
261
+ def set_tone(bass_db, treble_db, bass_cutoff_hz, treble_cutoff_hz)
262
+ Lib.rb_player_set_tone(
263
+ @ptr, Integer(bass_db), Integer(treble_db),
264
+ Integer(bass_cutoff_hz), Integer(treble_cutoff_hz)
265
+ )
266
+ self
267
+ end
268
+
269
+ # Bass gain in dB.
270
+ def set_bass(bass_db)
271
+ Lib.rb_player_set_bass(@ptr, Integer(bass_db))
272
+ self
273
+ end
274
+
275
+ # Treble gain in dB.
276
+ def set_treble(treble_db)
277
+ Lib.rb_player_set_treble(@ptr, Integer(treble_db))
278
+ self
279
+ end
280
+
281
+ # Bass tone-control cutoff frequency in Hz.
282
+ def set_bass_cutoff(hz)
283
+ Lib.rb_player_set_bass_cutoff(@ptr, Integer(hz))
284
+ self
285
+ end
286
+
287
+ # Treble tone-control cutoff frequency in Hz.
288
+ def set_treble_cutoff(hz)
289
+ Lib.rb_player_set_treble_cutoff(@ptr, Integer(hz))
290
+ self
291
+ end
292
+
293
+ # Crossfeed for headphone listening. +mode+ (see CrossfeedMode: OFF=0,
294
+ # MEIER=1, CUSTOM=2), plus direct/cross/high-frequency gains and the
295
+ # high-frequency cutoff (Hz) used in CUSTOM mode.
296
+ def set_crossfeed(mode, direct_gain, cross_gain, hf_gain, hf_cutoff)
297
+ Lib.rb_player_set_crossfeed(
298
+ @ptr, Integer(mode), Integer(direct_gain), Integer(cross_gain),
299
+ Integer(hf_gain), Integer(hf_cutoff)
300
+ )
301
+ self
302
+ end
303
+
304
+ # Bass enhancement: +strength+ and +precut+ (in dB).
305
+ def set_bass_enhancement(strength, precut)
306
+ Lib.rb_player_set_bass_enhancement(@ptr, Integer(strength), Integer(precut))
307
+ self
308
+ end
309
+
310
+ # Listening-fatigue reduction (treble roll-off): +strength+.
311
+ def set_fatigue_reduction(strength)
312
+ Lib.rb_player_set_fatigue_reduction(@ptr, Integer(strength))
313
+ self
314
+ end
315
+
316
+ # Surround effect: delay (ms), balance, low/high cutoff frequencies (Hz).
317
+ def set_surround(delay_ms, balance, cutoff_low_hz, cutoff_high_hz)
318
+ Lib.rb_player_set_surround(
319
+ @ptr, Integer(delay_ms), Integer(balance),
320
+ Integer(cutoff_low_hz), Integer(cutoff_high_hz)
321
+ )
322
+ self
323
+ end
324
+
325
+ # Channel mode (see ChannelMode).
326
+ def set_channel_mode(mode)
327
+ Lib.rb_player_set_channel_mode(@ptr, Integer(mode))
328
+ self
329
+ end
330
+
331
+ # Stereo width as a percentage.
332
+ def set_stereo_width(percent)
333
+ Lib.rb_player_set_stereo_width(@ptr, Integer(percent))
334
+ self
335
+ end
336
+
337
+ # Dynamic-range compressor: threshold (dB), makeup gain, ratio, knee,
338
+ # attack (ms), release (ms).
339
+ def set_compressor(threshold_db, makeup_gain, ratio, knee, attack_ms, release_ms)
340
+ Lib.rb_player_set_compressor(
341
+ @ptr, Integer(threshold_db), Integer(makeup_gain), Integer(ratio),
342
+ Integer(knee), Integer(attack_ms), Integer(release_ms)
343
+ )
344
+ self
345
+ end
346
+
347
+ # Enable/disable output dithering.
348
+ def set_dither(enabled)
349
+ Lib.rb_player_set_dither(@ptr, RockboxFFI.b(enabled))
350
+ self
351
+ end
352
+
353
+ # Pitch shift ratio.
354
+ def set_pitch(ratio)
355
+ Lib.rb_player_set_pitch(@ptr, Integer(ratio))
356
+ self
357
+ end
358
+
359
+ # A snapshot of the current DSP settings as a Hash with symbol keys.
360
+ def dsp_settings
361
+ s = RockboxFFI.take_string(Lib.rb_player_dsp_settings_json(@ptr))
362
+ raise "rb_player_dsp_settings_json returned NULL" if s.nil?
363
+
364
+ JSON.parse(s, symbolize_names: true)
171
365
  end
172
366
 
173
367
  # -- status -----------------------------------------------------------
@@ -1,3 +1,3 @@
1
1
  module RockboxFFI
2
- VERSION = "0.2.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/rockbox_ffi.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # Ruby bindings for the Rockbox DSP / metadata / playback engine.
4
4
  #
5
5
  # Thin Fiddle wrappers over the librockbox_ffi C ABI. See RockboxFFI::Metadata,
6
- # RockboxFFI::Dsp, and RockboxFFI::Player.
6
+ # RockboxFFI::Dsp, RockboxFFI::Player, and RockboxFFI::Decoder.
7
7
 
8
8
  require "rockbox_ffi/version"
9
9
  require "rockbox_ffi/ffi"
@@ -11,6 +11,7 @@ require "rockbox_ffi/enums"
11
11
  require "rockbox_ffi/metadata"
12
12
  require "rockbox_ffi/dsp"
13
13
  require "rockbox_ffi/player"
14
+ require "rockbox_ffi/decoder"
14
15
 
15
16
  require "json"
16
17
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rockbox_ffi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tsiry Sandratraina
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-12 00:00:00.000000000 Z
11
+ date: 2026-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fiddle
@@ -37,6 +37,7 @@ files:
37
37
  - examples/play.rb
38
38
  - examples/smoke.rb
39
39
  - lib/rockbox_ffi.rb
40
+ - lib/rockbox_ffi/decoder.rb
40
41
  - lib/rockbox_ffi/dsp.rb
41
42
  - lib/rockbox_ffi/enums.rb
42
43
  - lib/rockbox_ffi/ffi.rb