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 +4 -4
- data/README.md +4 -0
- data/examples/play.rb +9 -2
- data/lib/rockbox_ffi/decoder.rb +113 -0
- data/lib/rockbox_ffi/enums.rb +50 -0
- data/lib/rockbox_ffi/ffi.rb +37 -0
- data/lib/rockbox_ffi/player.rb +194 -0
- data/lib/rockbox_ffi/version.rb +1 -1
- data/lib/rockbox_ffi.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9e881324e0bb82a716a98417325a1ec9823d3a0f1f015444c6e2332f7b942c65
|
|
4
|
+
data.tar.gz: 70e45aa728fb5d2449e1849f993780a0dad201f32113eb4f352344df0bed1fb6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
17
|
-
|
|
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
|
data/lib/rockbox_ffi/enums.rb
CHANGED
|
@@ -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
|
data/lib/rockbox_ffi/ffi.rb
CHANGED
|
@@ -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*)"
|
data/lib/rockbox_ffi/player.rb
CHANGED
|
@@ -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 -----------------------------------------------------------
|
data/lib/rockbox_ffi/version.rb
CHANGED
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::
|
|
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.
|
|
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-
|
|
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
|