Hello there
My name is Raj and this is my first post here.
I’m mostly acquainted with Java and I’ve been learning Clojure for a few days now. I’m trying to write a simple app to read ID3v2 tags from an MP3 file using just the core libs. You can have a look here to see how the ID3v2 header is structured.
Here’s what I want to do:
- Read first 10 bytes from the MP3 file
- First three bytes should represent
\I \D \3
… so it should always be0x494433
- Store 4th and 5th bytes as version:
0x0300
means version3.0
- Use 6th byte to set various flags
- Last 4 bytes can be used to calculate total size
Mostly bit operations there. I’m searching for an idiomatic way of reading the bytes from io/input-stream
, performing the checks and storing valid results in a map. My initial attempt was something like:
(defn read-tags
[path]
(with-open [stream (io/input-stream path)]
(-> {:stream stream :result {}}
(read-id3)
(read-version)
(read-flags)
(read-size)
(:result))))
Where each of those read functions will read bytes from the stream, and update the results. And in the end, I can return that result map. For example, my read-id3
and read-version
look like:
(defn- read-id3
[stream-map]
(let [head (->> #(.read (:stream stream-map))
(repeatedly 3)
(map char)
(apply str))]
(if (= head "ID3")
stream-map
(throw (ex-info "Invalid ID3v2 header"
{:starts-with head})))))
(defn- read-version
[stream-map]
(let [stream (:stream stream-map)
major (.read stream)
revision (.read stream)]
(if (and (< major 0xFF) (< revision 0xFF))
(assoc-in stream-map [:result :version]
(str "ID3v2." major "." revision))
(throw (ex-info "Invalid ID3v2 version"
{:major major :revision revision})))))
How can this be improved and made more idiomatic?
How’d you have attempted the same problem?
Let me know your thoughts.