musikr: basic vorbis support

This commit is contained in:
Alexander Capehart 2025-02-08 19:52:10 -07:00
parent 013f25f46f
commit 3aa39a7065
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
5 changed files with 128 additions and 8 deletions

View file

@ -110,6 +110,7 @@ fn main() {
cxx_build::bridge("src/taglib/ffi.rs")
.file("shim/iostream_shim.cpp")
.file("shim/file_shim.cpp")
.file("shim/tk_shim.cpp")
.include(format!("taglib/pkg/{}/include", arch))
.include("shim")
.include(".") // Add the current directory to include path
@ -122,5 +123,7 @@ fn main() {
println!("cargo:rerun-if-changed=shim/iostream_shim.cpp");
println!("cargo:rerun-if-changed=shim/file_shim.hpp");
println!("cargo:rerun-if-changed=shim/file_shim.cpp");
println!("cargo:rerun-if-changed=shim/tk_shim.hpp");
println!("cargo:rerun-if-changed=shim/tk_shim.cpp");
println!("cargo:rerun-if-changed=src/taglib/ffi.rs");
}

View file

@ -0,0 +1,30 @@
#include "tk_shim.hpp"
namespace taglib_shim {
Property::Property(TagLib::String key, TagLib::StringList value) : key_(key), value_(value) {}
const TagLib::String &Property::key() const {
return key_;
}
const TagLib::StringList &Property::value() const {
return value_;
}
std::unique_ptr<std::vector<Property>> SimplePropertyMap_to_vector(const TagLib::SimplePropertyMap &map) {
std::unique_ptr<std::vector<Property>> result = std::make_unique<std::vector<Property>>();
for (const auto &pair : map) {
result->push_back(Property(pair.first, pair.second));
}
return result;
}
std::unique_ptr<std::vector<TagLib::String>> StringList_to_vector(const TagLib::StringList &list) {
std::unique_ptr<std::vector<TagLib::String>> result = std::make_unique<std::vector<TagLib::String>>();
for (const auto &str : list) {
result->push_back(str);
}
return result;
}
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "taglib/tpropertymap.h"
#include "taglib/xiphcomment.h"
#include "taglib/tstring.h"
#include "taglib/tstringlist.h"
#include <memory>
#include <iterator>
namespace taglib_shim {
struct Property {
Property(TagLib::String key, TagLib::StringList value);
const TagLib::String &key() const;
const TagLib::StringList &value() const;
private:
TagLib::String key_;
TagLib::StringList value_;
};
std::unique_ptr<std::vector<Property>> SimplePropertyMap_to_vector(const TagLib::SimplePropertyMap &map);
std::unique_ptr<std::vector<TagLib::String>> StringList_to_vector(const TagLib::StringList &list);
}

View file

@ -3,10 +3,12 @@ pub(crate) mod bindings {
unsafe extern "C++" {
include!("taglib/taglib.h");
include!("taglib/tstring.h");
include!("taglib/tstringlist.h");
include!("taglib/vorbisfile.h");
include!("taglib/xiphcomment.h");
include!("shim/iostream_shim.hpp");
include!("shim/file_shim.hpp");
include!("shim/tk_shim.hpp");
#[namespace = "TagLib"]
type FileRef;
@ -47,13 +49,11 @@ pub(crate) mod bindings {
#[namespace = "TagLib::Ogg::Opus"]
#[cxx_name = "File"]
type OpusFile;
unsafe fn tag(self: Pin<&OpusFile>) -> *mut XiphComment;
#[namespace = "TagLib::Ogg"]
type XiphComment;
unsafe fn fieldListMap(self: Pin<&XiphComment>) -> &FieldListMap;
#[namespace = "TagLib::Ogg"]
type FieldListMap;
unsafe fn fieldListMap(self: Pin<&XiphComment>) -> &SimplePropertyMap;
#[namespace = "TagLib::MPEG"]
#[cxx_name = "File"]
@ -94,6 +94,21 @@ pub(crate) mod bindings {
#[namespace = "taglib_shim"]
unsafe fn File_asAPE(file: *mut File) -> *mut APEFile;
#[namespace = "TagLib"]
type SimplePropertyMap;
#[namespace = "taglib_shim"]
fn SimplePropertyMap_to_vector(field_list_map: Pin<&SimplePropertyMap>) -> UniquePtr<CxxVector<Property>>;
#[namespace = "taglib_shim"]
type Property;
fn key(self: Pin<&Property>) -> &TagString;
unsafe fn value(self: Pin<&Property>) -> &StringList;
#[namespace = "TagLib"]
type StringList;
#[namespace = "taglib_shim"]
fn StringList_to_vector(string_list: Pin<&StringList>) -> UniquePtr<CxxVector<TagString>>;
#[namespace = "TagLib"]
#[cxx_name = "String"]
type TagString;

View file

@ -2,9 +2,13 @@ mod ffi;
mod stream;
use ffi::bindings;
use std::pin::Pin;
use std::ffi::CStr;
use std::pin::{pin, Pin};
use std::collections::HashMap;
pub use stream::{RustStream, TagLibStream};
type XiphComments = HashMap<String, Vec<String>>;
pub enum File {
Unknown {
audio_properties: Option<AudioProperties>,
@ -14,15 +18,18 @@ pub enum File {
},
FLAC {
audio_properties: Option<AudioProperties>,
xiph_comments: Option<XiphComments>,
},
MP4 {
audio_properties: Option<AudioProperties>,
},
OGG {
audio_properties: Option<AudioProperties>,
xiph_comments: Option<XiphComments>,
},
Opus {
audio_properties: Option<AudioProperties>,
xiph_comments: Option<XiphComments>,
},
WAV {
audio_properties: Option<AudioProperties>,
@ -100,7 +107,7 @@ impl FileRef {
let flac_file = ffi::bindings::File_asFLAC(file_ptr);
if !flac_file.is_null() {
return Some(FileRef {
file: File::FLAC { audio_properties }
file: File::FLAC { audio_properties, xiph_comments: None }
});
}
@ -134,15 +141,56 @@ impl FileRef {
let vorbis_file = ffi::bindings::File_asVorbis(file_ptr);
if !vorbis_file.is_null() {
let pinned_vorbis_file = Pin::new_unchecked(&*vorbis_file);
let xiph_comments = pinned_vorbis_file.tag();
let pinned_xiph_comments = Pin::new_unchecked(&*xiph_comments);
let xiph_map = pinned_xiph_comments.fieldListMap();
let pinned_xiph_map = Pin::new_unchecked(xiph_map);
let xiph_comments = ffi::bindings::SimplePropertyMap_to_vector(pinned_xiph_map);
let mut xiph_safe_map = XiphComments::new();
for property in xiph_comments.iter() {
let pinned_property = Pin::new_unchecked(property);
let tag_key = pinned_property.key();
let pinned_key = Pin::new_unchecked(&*tag_key);
let c_str = pinned_key.toCString(true);
let key = CStr::from_ptr(c_str).to_string_lossy().to_string();
let tag_values = pinned_property.value();
let pinned_values = Pin::new_unchecked(&*tag_values);
let cxx_vec_values = ffi::bindings::StringList_to_vector(pinned_values);
let values = cxx_vec_values.iter().map(|value| {
let pinned_value = Pin::new_unchecked(value);
let c_str = pinned_value.toCString(true);
CStr::from_ptr(c_str).to_string_lossy().to_string()
}).collect();
xiph_safe_map.insert(key, values);
}
return Some(FileRef {
file: File::OGG { audio_properties }
file: File::OGG { audio_properties, xiph_comments: Some(xiph_safe_map) }
});
}
let opus_file = ffi::bindings::File_asOpus(file_ptr);
if !opus_file.is_null() {
let pinned_opus_file = Pin::new_unchecked(&*opus_file);
let xiph_comments = pinned_opus_file.tag();
let pinned_xiph_comments = Pin::new_unchecked(&*xiph_comments);
let xiph_map = pinned_xiph_comments.fieldListMap();
let pinned_xiph_map = Pin::new_unchecked(xiph_map);
let xiph_comments = ffi::bindings::SimplePropertyMap_to_vector(pinned_xiph_map);
let mut xiph_safe_map = XiphComments::new();
for property in xiph_comments.iter() {
let pinned_property = Pin::new_unchecked(property);
let tag_key = pinned_property.key();
let pinned_key = Pin::new_unchecked(&*tag_key);
}
return Some(FileRef {
file: File::Opus { audio_properties }
file: File::Opus { audio_properties, xiph_comments: Some(xiph_safe_map) }
});
}