rockbox_ffi 0.1.2 → 0.3.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 +19 -1
- data/examples/play.rb +9 -2
- data/lib/rockbox_ffi/enums.rb +63 -0
- data/lib/rockbox_ffi/ffi.rb +43 -0
- data/lib/rockbox_ffi/player.rb +254 -4
- data/lib/rockbox_ffi/version.rb +1 -3
- data/lib/rockbox_ffi.rb +32 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65435e6c7a7b4ef8e59f3bc088ce2c9eb978b34d49e134c3e42413e9790bb333
|
|
4
|
+
data.tar.gz: 0d14838882902eaa6818a9726518f0c782848ad34e09b6afe6ca9ddbbf6cdd1a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f4275075ebc6d35b7b495dfc3a081b97960f5dc81f8f279372acf5b852fc0723776f536f9f87602811499a23d1653de9a8c5b072144444fe15dd0cd4b4dc1066
|
|
7
|
+
data.tar.gz: 5b4a35ca244aed1f570cf2d639a131eb8244645fe4ebe86513193345a95716107c72c67f7037f195e8048bff4f885c1d3c8af1fdd218ffabcf200860b841e363
|
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):
|
|
@@ -95,7 +99,7 @@ ruby -Ilib examples/play.rb [path] # play a file through the output device
|
|
|
95
99
|
## Interactive console
|
|
96
100
|
|
|
97
101
|
```sh
|
|
98
|
-
|
|
102
|
+
bundle exec rake console # or: ./bin/console
|
|
99
103
|
```
|
|
100
104
|
|
|
101
105
|
Drops into IRB with `RockboxFFI` loaded and `FIXTURE` pointing at a sample
|
|
@@ -108,6 +112,20 @@ p.set_queue([FIXTURE]); p.play
|
|
|
108
112
|
p.status[:state] # => "playing"
|
|
109
113
|
```
|
|
110
114
|
|
|
115
|
+
The console bundles `irb` + `reline`, so **Tab autocompletion** and **syntax
|
|
116
|
+
highlighting** work out of the box — start typing `RockboxFFI::` and press
|
|
117
|
+
`Tab`. Both are on by default; toggle per-session with `irb --noautocomplete`,
|
|
118
|
+
or persist preferences in `~/.irbrc`:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
IRB.conf[:USE_AUTOCOMPLETE] = true
|
|
122
|
+
IRB.conf[:USE_COLORIZE] = true
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
> Run the console under a modern Ruby (3.x/4.x, e.g. Homebrew's
|
|
126
|
+
> `/opt/homebrew/opt/ruby`). macOS system Ruby 2.6 can't build `fiddle`'s
|
|
127
|
+
> native extension, so `bundle install` fails there.
|
|
128
|
+
|
|
111
129
|
## Test
|
|
112
130
|
|
|
113
131
|
```sh
|
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
|
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
|
|
@@ -32,6 +39,19 @@ module RockboxFFI
|
|
|
32
39
|
MIX = 1
|
|
33
40
|
end
|
|
34
41
|
|
|
42
|
+
# Where inserted tracks land in the queue (Player#insert / #import_m3u).
|
|
43
|
+
# INDEX (7) uses the explicit +index+ argument.
|
|
44
|
+
module InsertPosition
|
|
45
|
+
PREPEND = 0
|
|
46
|
+
INSERT = 1
|
|
47
|
+
INSERT_NEXT = 2
|
|
48
|
+
INSERT_LAST = 3
|
|
49
|
+
INSERT_SHUFFLED = 4
|
|
50
|
+
INSERT_LAST_SHUFFLED = 5
|
|
51
|
+
REPLACE = 6
|
|
52
|
+
INDEX = 7
|
|
53
|
+
end
|
|
54
|
+
|
|
35
55
|
module ChannelConfig
|
|
36
56
|
STEREO = 0
|
|
37
57
|
MONO = 1
|
|
@@ -41,4 +61,47 @@ module RockboxFFI
|
|
|
41
61
|
KARAOKE = 5
|
|
42
62
|
SWAP = 6
|
|
43
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
|
|
44
107
|
end
|
data/lib/rockbox_ffi/ffi.rb
CHANGED
|
@@ -101,9 +101,12 @@ module RockboxFFI
|
|
|
101
101
|
# ---- player -------------------------------------------------------
|
|
102
102
|
extern "void* rb_player_new()"
|
|
103
103
|
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)"
|
|
104
|
+
extern "void* rb_player_new_with_config_ex(uint32_t, float, float, int32_t, float, bool, int32_t, uint32_t, uint32_t, uint32_t, uint32_t, int32_t, void*, uint32_t)"
|
|
104
105
|
extern "void rb_player_free(void*)"
|
|
105
106
|
extern "void rb_player_set_queue_json(void*, void*)"
|
|
106
107
|
extern "void rb_player_enqueue(void*, void*)"
|
|
108
|
+
extern "void rb_player_insert_json(void*, void*, int32_t, size_t)"
|
|
109
|
+
extern "void* rb_player_queue_json(void*)"
|
|
107
110
|
extern "void rb_player_play(void*)"
|
|
108
111
|
extern "void rb_player_pause(void*)"
|
|
109
112
|
extern "void rb_player_toggle(void*)"
|
|
@@ -118,6 +121,46 @@ module RockboxFFI
|
|
|
118
121
|
extern "float rb_player_volume(void*)"
|
|
119
122
|
extern "uint32_t rb_player_sample_rate(void*)"
|
|
120
123
|
extern "void* rb_player_status_json(void*)"
|
|
124
|
+
extern "void rb_player_set_shuffle(void*, bool)"
|
|
125
|
+
extern "bool rb_player_is_shuffle_enabled(void*)"
|
|
126
|
+
extern "void rb_player_set_repeat(void*, int32_t)"
|
|
127
|
+
extern "int32_t rb_player_repeat(void*)"
|
|
128
|
+
|
|
129
|
+
# ---- player DSP ---------------------------------------------------
|
|
130
|
+
extern "void rb_player_set_eq_enabled(void*, bool)"
|
|
131
|
+
extern "bool rb_player_is_eq_enabled(void*)"
|
|
132
|
+
extern "void rb_player_set_eq_band(void*, size_t, int32_t, float, float)"
|
|
133
|
+
extern "void rb_player_set_eq_precut(void*, float)"
|
|
134
|
+
extern "void rb_player_set_eq_preset(void*, int32_t)"
|
|
135
|
+
extern "void rb_player_set_tone(void*, int32_t, int32_t, int32_t, int32_t)"
|
|
136
|
+
extern "void rb_player_set_bass(void*, int32_t)"
|
|
137
|
+
extern "void rb_player_set_treble(void*, int32_t)"
|
|
138
|
+
extern "void rb_player_set_bass_cutoff(void*, int32_t)"
|
|
139
|
+
extern "void rb_player_set_treble_cutoff(void*, int32_t)"
|
|
140
|
+
extern "void rb_player_set_crossfeed(void*, int32_t, int32_t, int32_t, int32_t, int32_t)"
|
|
141
|
+
extern "void rb_player_set_bass_enhancement(void*, int32_t, int32_t)"
|
|
142
|
+
extern "void rb_player_set_fatigue_reduction(void*, int32_t)"
|
|
143
|
+
extern "void rb_player_set_surround(void*, int32_t, int32_t, int32_t, int32_t)"
|
|
144
|
+
extern "void rb_player_set_channel_mode(void*, int32_t)"
|
|
145
|
+
extern "void rb_player_set_stereo_width(void*, int32_t)"
|
|
146
|
+
extern "void rb_player_set_compressor(void*, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t)"
|
|
147
|
+
extern "void rb_player_set_dither(void*, bool)"
|
|
148
|
+
extern "void rb_player_set_pitch(void*, int32_t)"
|
|
149
|
+
extern "void* rb_player_dsp_settings_json(void*)"
|
|
150
|
+
|
|
151
|
+
# ---- resume -------------------------------------------------------
|
|
152
|
+
extern "void* rb_player_resume(void*)"
|
|
153
|
+
extern "void rb_player_save_resume(void*)"
|
|
154
|
+
extern "void rb_player_clear_resume(void*)"
|
|
155
|
+
extern "void* rb_load_resume_json(void*)"
|
|
156
|
+
|
|
157
|
+
# ---- m3u / m3u8 playlists -----------------------------------------
|
|
158
|
+
extern "void* rb_player_import_m3u(void*, void*, int32_t, size_t)"
|
|
159
|
+
extern "void* rb_player_load_m3u(void*, void*)"
|
|
160
|
+
extern "int32_t rb_player_export_m3u(void*, void*)"
|
|
161
|
+
extern "void* rb_m3u_read_json(void*)"
|
|
162
|
+
extern "int32_t rb_m3u_write_json(void*, void*)"
|
|
163
|
+
extern "bool rb_is_url(void*)"
|
|
121
164
|
end
|
|
122
165
|
|
|
123
166
|
# true/false → 1/0 for the ABI's `bool` (declared as int above).
|
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
|
|
@@ -24,7 +29,9 @@ module RockboxFFI
|
|
|
24
29
|
fade_out_duration_ms: 2000,
|
|
25
30
|
fade_in_delay_ms: 0,
|
|
26
31
|
fade_in_duration_ms: 2000,
|
|
27
|
-
mix_mode: MixMode::CROSSFADE
|
|
32
|
+
mix_mode: MixMode::CROSSFADE,
|
|
33
|
+
resume_file: nil, # an .m3u8 to auto-persist queue + position to
|
|
34
|
+
resume_save_interval_ms: 0 # 0 => 5 s default
|
|
28
35
|
}.freeze
|
|
29
36
|
|
|
30
37
|
# Open a Player; if a block is given, close it automatically afterwards.
|
|
@@ -47,16 +54,19 @@ module RockboxFFI
|
|
|
47
54
|
end
|
|
48
55
|
|
|
49
56
|
# Create a player with configuration overrides (see DEFAULT_CONFIG keys).
|
|
50
|
-
# sample_rate: 0 means the device default.
|
|
57
|
+
# sample_rate: 0 means the device default. Passing +resume_file:+ enables
|
|
58
|
+
# auto-persisting the queue + exact position to that .m3u8 file.
|
|
51
59
|
def initialize(**opts)
|
|
52
60
|
c = DEFAULT_CONFIG.merge(opts)
|
|
53
|
-
|
|
61
|
+
resume_file = c[:resume_file].nil? ? nil : c[:resume_file].to_s
|
|
62
|
+
ptr = Lib.rb_player_new_with_config_ex(
|
|
54
63
|
Integer(c[:sample_rate]), Float(c[:buffer_seconds]), Float(c[:volume]),
|
|
55
64
|
Integer(c[:replaygain_mode]), Float(c[:replaygain_preamp_db]),
|
|
56
65
|
RockboxFFI.b(c[:replaygain_prevent_clipping]), Integer(c[:crossfade_mode]),
|
|
57
66
|
Integer(c[:fade_out_delay_ms]), Integer(c[:fade_out_duration_ms]),
|
|
58
67
|
Integer(c[:fade_in_delay_ms]), Integer(c[:fade_in_duration_ms]),
|
|
59
|
-
Integer(c[:mix_mode])
|
|
68
|
+
Integer(c[:mix_mode]), resume_file,
|
|
69
|
+
Integer(c[:resume_save_interval_ms])
|
|
60
70
|
)
|
|
61
71
|
init_ptr(ptr)
|
|
62
72
|
end
|
|
@@ -80,50 +90,83 @@ module RockboxFFI
|
|
|
80
90
|
private_class_method :finalizer
|
|
81
91
|
|
|
82
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.
|
|
83
95
|
def set_queue(paths)
|
|
84
96
|
Lib.rb_player_set_queue_json(@ptr, JSON.generate(Array(paths).map(&:to_s)))
|
|
97
|
+
self
|
|
85
98
|
end
|
|
86
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.
|
|
87
102
|
def enqueue(path)
|
|
88
103
|
Lib.rb_player_enqueue(@ptr, path.to_s)
|
|
104
|
+
self
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Insert +paths+ (a path/URL or Array of them) into the queue at
|
|
108
|
+
# +position+ (see InsertPosition). +index+ is only used when position is
|
|
109
|
+
# InsertPosition::INDEX (7).
|
|
110
|
+
def insert(paths, position, index = 0)
|
|
111
|
+
Lib.rb_player_insert_json(
|
|
112
|
+
@ptr, JSON.generate(Array(paths).map(&:to_s)), Integer(position), Integer(index)
|
|
113
|
+
)
|
|
114
|
+
self
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# The current queue as an Array of String paths/URLs.
|
|
118
|
+
def queue
|
|
119
|
+
s = RockboxFFI.take_string(Lib.rb_player_queue_json(@ptr))
|
|
120
|
+
return [] if s.nil?
|
|
121
|
+
|
|
122
|
+
JSON.parse(s)
|
|
89
123
|
end
|
|
90
124
|
|
|
91
125
|
# -- transport --------------------------------------------------------
|
|
92
126
|
def play
|
|
93
127
|
Lib.rb_player_play(@ptr)
|
|
128
|
+
self
|
|
94
129
|
end
|
|
95
130
|
|
|
96
131
|
def pause
|
|
97
132
|
Lib.rb_player_pause(@ptr)
|
|
133
|
+
self
|
|
98
134
|
end
|
|
99
135
|
|
|
100
136
|
def toggle
|
|
101
137
|
Lib.rb_player_toggle(@ptr)
|
|
138
|
+
self
|
|
102
139
|
end
|
|
103
140
|
|
|
104
141
|
def stop
|
|
105
142
|
Lib.rb_player_stop(@ptr)
|
|
143
|
+
self
|
|
106
144
|
end
|
|
107
145
|
|
|
108
146
|
def next
|
|
109
147
|
Lib.rb_player_next(@ptr)
|
|
148
|
+
self
|
|
110
149
|
end
|
|
111
150
|
|
|
112
151
|
def previous
|
|
113
152
|
Lib.rb_player_previous(@ptr)
|
|
153
|
+
self
|
|
114
154
|
end
|
|
115
155
|
|
|
116
156
|
def skip_to(index)
|
|
117
157
|
Lib.rb_player_skip_to(@ptr, Integer(index))
|
|
158
|
+
self
|
|
118
159
|
end
|
|
119
160
|
|
|
120
161
|
def seek_ms(ms)
|
|
121
162
|
Lib.rb_player_seek_ms(@ptr, Integer(ms))
|
|
163
|
+
self
|
|
122
164
|
end
|
|
123
165
|
|
|
124
166
|
# -- settings ---------------------------------------------------------
|
|
125
167
|
def set_volume(vol)
|
|
126
168
|
Lib.rb_player_set_volume(@ptr, Float(vol))
|
|
169
|
+
self
|
|
127
170
|
end
|
|
128
171
|
|
|
129
172
|
def volume
|
|
@@ -141,11 +184,173 @@ module RockboxFFI
|
|
|
141
184
|
@ptr, Integer(mode), Integer(fade_out_delay_ms), Integer(fade_out_duration_ms),
|
|
142
185
|
Integer(fade_in_delay_ms), Integer(fade_in_duration_ms), Integer(mix_mode)
|
|
143
186
|
)
|
|
187
|
+
self
|
|
144
188
|
end
|
|
145
189
|
|
|
146
190
|
# mode: see ReplayGainMode (OFF=0, TRACK=1, ALBUM=2).
|
|
147
191
|
def set_replaygain(mode, preamp_db, prevent_clipping)
|
|
148
192
|
Lib.rb_player_set_replaygain(@ptr, Integer(mode), Float(preamp_db), RockboxFFI.b(prevent_clipping))
|
|
193
|
+
self
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Enable/disable shuffle.
|
|
197
|
+
def set_shuffle(enabled)
|
|
198
|
+
Lib.rb_player_set_shuffle(@ptr, RockboxFFI.b(enabled))
|
|
199
|
+
self
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Whether shuffle is currently enabled.
|
|
203
|
+
def shuffle_enabled?
|
|
204
|
+
!Lib.rb_player_is_shuffle_enabled(@ptr).zero?
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Set the repeat mode (see RepeatMode: OFF=0, ONE=1, ALL=2).
|
|
208
|
+
def set_repeat(mode)
|
|
209
|
+
Lib.rb_player_set_repeat(@ptr, Integer(mode))
|
|
210
|
+
self
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# The current repeat mode as an Integer (see RepeatMode).
|
|
214
|
+
def repeat
|
|
215
|
+
Lib.rb_player_repeat(@ptr)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# -- DSP --------------------------------------------------------------
|
|
219
|
+
# Enable/disable the parametric equalizer.
|
|
220
|
+
def set_eq_enabled(enabled)
|
|
221
|
+
Lib.rb_player_set_eq_enabled(@ptr, RockboxFFI.b(enabled))
|
|
222
|
+
self
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Whether the parametric equalizer is currently enabled.
|
|
226
|
+
def eq_enabled?
|
|
227
|
+
!Lib.rb_player_is_eq_enabled(@ptr).zero?
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Configure one EQ band: +band+ index, +cutoff_hz+ center frequency,
|
|
231
|
+
# +q+ factor, +gain_db+ gain in dB.
|
|
232
|
+
def set_eq_band(band, cutoff_hz, q, gain_db)
|
|
233
|
+
Lib.rb_player_set_eq_band(@ptr, Integer(band), Integer(cutoff_hz), Float(q), Float(gain_db))
|
|
234
|
+
self
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Global EQ pre-cut in dB.
|
|
238
|
+
def set_eq_precut(db)
|
|
239
|
+
Lib.rb_player_set_eq_precut(@ptr, Float(db))
|
|
240
|
+
self
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Apply a built-in EQ preset (see EqPreset).
|
|
244
|
+
def set_eq_preset(preset)
|
|
245
|
+
Lib.rb_player_set_eq_preset(@ptr, Integer(preset))
|
|
246
|
+
self
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Bass/treble tone controls with explicit cutoff frequencies.
|
|
250
|
+
def set_tone(bass_db, treble_db, bass_cutoff_hz, treble_cutoff_hz)
|
|
251
|
+
Lib.rb_player_set_tone(
|
|
252
|
+
@ptr, Integer(bass_db), Integer(treble_db),
|
|
253
|
+
Integer(bass_cutoff_hz), Integer(treble_cutoff_hz)
|
|
254
|
+
)
|
|
255
|
+
self
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Bass gain in dB.
|
|
259
|
+
def set_bass(bass_db)
|
|
260
|
+
Lib.rb_player_set_bass(@ptr, Integer(bass_db))
|
|
261
|
+
self
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Treble gain in dB.
|
|
265
|
+
def set_treble(treble_db)
|
|
266
|
+
Lib.rb_player_set_treble(@ptr, Integer(treble_db))
|
|
267
|
+
self
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Bass tone-control cutoff frequency in Hz.
|
|
271
|
+
def set_bass_cutoff(hz)
|
|
272
|
+
Lib.rb_player_set_bass_cutoff(@ptr, Integer(hz))
|
|
273
|
+
self
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Treble tone-control cutoff frequency in Hz.
|
|
277
|
+
def set_treble_cutoff(hz)
|
|
278
|
+
Lib.rb_player_set_treble_cutoff(@ptr, Integer(hz))
|
|
279
|
+
self
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Crossfeed for headphone listening. +mode+ (see CrossfeedMode: OFF=0,
|
|
283
|
+
# MEIER=1, CUSTOM=2), plus direct/cross/high-frequency gains and the
|
|
284
|
+
# high-frequency cutoff (Hz) used in CUSTOM mode.
|
|
285
|
+
def set_crossfeed(mode, direct_gain, cross_gain, hf_gain, hf_cutoff)
|
|
286
|
+
Lib.rb_player_set_crossfeed(
|
|
287
|
+
@ptr, Integer(mode), Integer(direct_gain), Integer(cross_gain),
|
|
288
|
+
Integer(hf_gain), Integer(hf_cutoff)
|
|
289
|
+
)
|
|
290
|
+
self
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Bass enhancement: +strength+ and +precut+ (in dB).
|
|
294
|
+
def set_bass_enhancement(strength, precut)
|
|
295
|
+
Lib.rb_player_set_bass_enhancement(@ptr, Integer(strength), Integer(precut))
|
|
296
|
+
self
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Listening-fatigue reduction (treble roll-off): +strength+.
|
|
300
|
+
def set_fatigue_reduction(strength)
|
|
301
|
+
Lib.rb_player_set_fatigue_reduction(@ptr, Integer(strength))
|
|
302
|
+
self
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Surround effect: delay (ms), balance, low/high cutoff frequencies (Hz).
|
|
306
|
+
def set_surround(delay_ms, balance, cutoff_low_hz, cutoff_high_hz)
|
|
307
|
+
Lib.rb_player_set_surround(
|
|
308
|
+
@ptr, Integer(delay_ms), Integer(balance),
|
|
309
|
+
Integer(cutoff_low_hz), Integer(cutoff_high_hz)
|
|
310
|
+
)
|
|
311
|
+
self
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Channel mode (see ChannelMode).
|
|
315
|
+
def set_channel_mode(mode)
|
|
316
|
+
Lib.rb_player_set_channel_mode(@ptr, Integer(mode))
|
|
317
|
+
self
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Stereo width as a percentage.
|
|
321
|
+
def set_stereo_width(percent)
|
|
322
|
+
Lib.rb_player_set_stereo_width(@ptr, Integer(percent))
|
|
323
|
+
self
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Dynamic-range compressor: threshold (dB), makeup gain, ratio, knee,
|
|
327
|
+
# attack (ms), release (ms).
|
|
328
|
+
def set_compressor(threshold_db, makeup_gain, ratio, knee, attack_ms, release_ms)
|
|
329
|
+
Lib.rb_player_set_compressor(
|
|
330
|
+
@ptr, Integer(threshold_db), Integer(makeup_gain), Integer(ratio),
|
|
331
|
+
Integer(knee), Integer(attack_ms), Integer(release_ms)
|
|
332
|
+
)
|
|
333
|
+
self
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Enable/disable output dithering.
|
|
337
|
+
def set_dither(enabled)
|
|
338
|
+
Lib.rb_player_set_dither(@ptr, RockboxFFI.b(enabled))
|
|
339
|
+
self
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Pitch shift ratio.
|
|
343
|
+
def set_pitch(ratio)
|
|
344
|
+
Lib.rb_player_set_pitch(@ptr, Integer(ratio))
|
|
345
|
+
self
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# A snapshot of the current DSP settings as a Hash with symbol keys.
|
|
349
|
+
def dsp_settings
|
|
350
|
+
s = RockboxFFI.take_string(Lib.rb_player_dsp_settings_json(@ptr))
|
|
351
|
+
raise "rb_player_dsp_settings_json returned NULL" if s.nil?
|
|
352
|
+
|
|
353
|
+
JSON.parse(s, symbolize_names: true)
|
|
149
354
|
end
|
|
150
355
|
|
|
151
356
|
# -- status -----------------------------------------------------------
|
|
@@ -157,6 +362,51 @@ module RockboxFFI
|
|
|
157
362
|
JSON.parse(s, symbolize_names: true)
|
|
158
363
|
end
|
|
159
364
|
|
|
365
|
+
# -- resume -----------------------------------------------------------
|
|
366
|
+
# Restore the queue + exact position from the resume file (does NOT
|
|
367
|
+
# auto-play). Returns a Hash {tracks:, index:, elapsed_ms:} or nil.
|
|
368
|
+
def resume
|
|
369
|
+
s = RockboxFFI.take_string(Lib.rb_player_resume(@ptr))
|
|
370
|
+
return nil if s.nil?
|
|
371
|
+
|
|
372
|
+
JSON.parse(s, symbolize_names: true)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Force-persist the current queue + position to the resume file now.
|
|
376
|
+
def save_resume
|
|
377
|
+
Lib.rb_player_save_resume(@ptr)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# Delete the resume file.
|
|
381
|
+
def clear_resume
|
|
382
|
+
Lib.rb_player_clear_resume(@ptr)
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# -- m3u / m3u8 playlists ---------------------------------------------
|
|
386
|
+
# Import a playlist file into the queue at +position+ (see InsertPosition;
|
|
387
|
+
# +index+ only used for INDEX). Returns the imported paths as an Array.
|
|
388
|
+
def import_m3u(path, position, index = 0)
|
|
389
|
+
s = RockboxFFI.take_string(
|
|
390
|
+
Lib.rb_player_import_m3u(@ptr, path.to_s, Integer(position), Integer(index))
|
|
391
|
+
)
|
|
392
|
+
return [] if s.nil?
|
|
393
|
+
|
|
394
|
+
JSON.parse(s)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Replace the queue with a playlist file. Returns the loaded paths as an Array.
|
|
398
|
+
def load_m3u(path)
|
|
399
|
+
s = RockboxFFI.take_string(Lib.rb_player_load_m3u(@ptr, path.to_s))
|
|
400
|
+
return [] if s.nil?
|
|
401
|
+
|
|
402
|
+
JSON.parse(s)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Export the current queue to an .m3u8 (atomic). Returns true on success.
|
|
406
|
+
def export_m3u(path)
|
|
407
|
+
Lib.rb_player_export_m3u(@ptr, path.to_s).zero?
|
|
408
|
+
end
|
|
409
|
+
|
|
160
410
|
private
|
|
161
411
|
|
|
162
412
|
def init_ptr(ptr)
|
data/lib/rockbox_ffi/version.rb
CHANGED
data/lib/rockbox_ffi.rb
CHANGED
|
@@ -12,9 +12,41 @@ require "rockbox_ffi/metadata"
|
|
|
12
12
|
require "rockbox_ffi/dsp"
|
|
13
13
|
require "rockbox_ffi/player"
|
|
14
14
|
|
|
15
|
+
require "json"
|
|
16
|
+
|
|
15
17
|
module RockboxFFI
|
|
16
18
|
# ABI major version of the loaded library (bumped on breaking changes).
|
|
17
19
|
def self.abi_version
|
|
18
20
|
Lib.rb_ffi_abi_version
|
|
19
21
|
end
|
|
22
|
+
|
|
23
|
+
# Peek at a resume file without a Player. Returns a Hash
|
|
24
|
+
# {tracks:, index:, elapsed_ms:} or nil if the file is absent/invalid.
|
|
25
|
+
def self.load_resume(path)
|
|
26
|
+
s = take_string(Lib.rb_load_resume_json(path.to_s))
|
|
27
|
+
return nil if s.nil?
|
|
28
|
+
|
|
29
|
+
JSON.parse(s, symbolize_names: true)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Parse a playlist (.m3u / .m3u8) into an Array of Hashes
|
|
33
|
+
# {path:, duration_ms:, title:}. Returns nil on failure.
|
|
34
|
+
def self.m3u_read(path)
|
|
35
|
+
s = take_string(Lib.rb_m3u_read_json(path.to_s))
|
|
36
|
+
return nil if s.nil?
|
|
37
|
+
|
|
38
|
+
JSON.parse(s, symbolize_names: true)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Write +paths+ (a path/URL or Array of them) as an .m3u8. Returns true on
|
|
42
|
+
# success.
|
|
43
|
+
def self.m3u_write(path, paths)
|
|
44
|
+
Lib.rb_m3u_write_json(path.to_s, JSON.generate(Array(paths).map(&:to_s))).zero?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Whether +s+ looks like an http(s):// URL.
|
|
48
|
+
def self.is_url?(s)
|
|
49
|
+
r = Lib.rb_is_url(s.to_s)
|
|
50
|
+
r == true || r == 1
|
|
51
|
+
end
|
|
20
52
|
end
|
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.3.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-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: fiddle
|