musikr: minimize ffi shims

This commit is contained in:
Alexander Capehart 2025-02-08 17:03:47 -07:00
parent cf597cb98e
commit acee4ddedd
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
9 changed files with 69 additions and 228 deletions

View file

@ -110,8 +110,6 @@ fn main() {
cxx_build::bridge("src/taglib/ffi.rs")
.file("shim/iostream_shim.cpp")
.file("shim/file_shim.cpp")
.file("shim/string_shim.cpp")
.file("shim/audioproperties_shim.cpp")
.include(format!("taglib/pkg/{}/include", arch))
.include("shim")
.include(".") // Add the current directory to include path
@ -124,9 +122,5 @@ 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/string_shim.hpp");
println!("cargo:rerun-if-changed=shim/string_shim.cpp");
println!("cargo:rerun-if-changed=shim/audioproperties_shim.hpp");
println!("cargo:rerun-if-changed=shim/audioproperties_shim.cpp");
println!("cargo:rerun-if-changed=src/taglib/ffi.rs");
}

View file

@ -1,26 +0,0 @@
#include "audioproperties_shim.hpp"
#include <taglib/tfile.h>
namespace taglib_shim {
const TagLib::AudioProperties* File_audioProperties(const TagLib::File& file) {
return file.audioProperties();
}
int AudioProperties_lengthInMilliseconds(const TagLib::AudioProperties* properties) {
return properties->lengthInMilliseconds();
}
int AudioProperties_bitrateInKilobitsPerSecond(const TagLib::AudioProperties* properties) {
return properties->bitrate();
}
int AudioProperties_sampleRateInHz(const TagLib::AudioProperties* properties) {
return properties->sampleRate();
}
int AudioProperties_numberOfChannels(const TagLib::AudioProperties* properties) {
return properties->channels();
}
} // namespace taglib_shim

View file

@ -1,15 +0,0 @@
#pragma once
#include <taglib/audioproperties.h>
#include <taglib/tfile.h>
namespace taglib_shim {
// Audio Properties methods
const TagLib::AudioProperties* File_audioProperties(const TagLib::File& file);
int AudioProperties_lengthInMilliseconds(const TagLib::AudioProperties* properties);
int AudioProperties_bitrateInKilobitsPerSecond(const TagLib::AudioProperties* properties);
int AudioProperties_sampleRateInHz(const TagLib::AudioProperties* properties);
int AudioProperties_numberOfChannels(const TagLib::AudioProperties* properties);
} // namespace taglib_shim

View file

@ -2,65 +2,37 @@
namespace taglib_shim {
// FileRef helper functions
bool FileRef_isNull(const TagLib::FileRef& ref) {
return ref.isNull();
}
const TagLib::File& FileRef_file(const TagLib::FileRef& ref) {
return *ref.file();
}
// File tag methods
bool File_tag(const TagLib::File& file) {
return file.tag() != nullptr;
}
namespace {
// Keep the empty string as a static member to ensure it lives long enough
const TagLib::String empty_string;
}
const TagLib::String& File_tag_title(const TagLib::File& file) {
if (auto* tag = file.tag()) {
static TagLib::String title;
title = tag->title();
return title;
}
return empty_string;
}
// File type checking functions
bool File_isMPEG(const TagLib::File& file) {
return dynamic_cast<const TagLib::MPEG::File*>(&file) != nullptr;
bool File_isMPEG(TagLib::File* file) {
return dynamic_cast<TagLib::MPEG::File*>(file) != nullptr;
}
bool File_isFLAC(const TagLib::File& file) {
return dynamic_cast<const TagLib::FLAC::File*>(&file) != nullptr;
bool File_isFLAC(TagLib::File* file) {
return dynamic_cast<TagLib::FLAC::File*>(file) != nullptr;
}
bool File_isMP4(const TagLib::File& file) {
return dynamic_cast<const TagLib::MP4::File*>(&file) != nullptr;
bool File_isMP4(TagLib::File* file) {
return dynamic_cast<TagLib::MP4::File*>(file) != nullptr;
}
bool File_isOgg(const TagLib::File& file) {
return dynamic_cast<const TagLib::Ogg::File*>(&file) != nullptr;
bool File_isOgg(TagLib::File* file) {
return dynamic_cast<TagLib::Ogg::File*>(file) != nullptr;
}
bool File_isOpus(const TagLib::File& file) {
return dynamic_cast<const TagLib::Ogg::Opus::File*>(&file) != nullptr;
bool File_isOpus(TagLib::File* file) {
return dynamic_cast<TagLib::Ogg::Opus::File*>(file) != nullptr;
}
bool File_isWAV(const TagLib::File& file) {
return dynamic_cast<const TagLib::RIFF::WAV::File*>(&file) != nullptr;
bool File_isWAV(TagLib::File* file) {
return dynamic_cast<TagLib::RIFF::WAV::File*>(file) != nullptr;
}
bool File_isWavPack(const TagLib::File& file) {
return dynamic_cast<const TagLib::WavPack::File*>(&file) != nullptr;
bool File_isWavPack(TagLib::File* file) {
return dynamic_cast<TagLib::WavPack::File*>(file) != nullptr;
}
bool File_isAPE(const TagLib::File& file) {
return dynamic_cast<const TagLib::APE::File*>(&file) != nullptr;
bool File_isAPE(TagLib::File* file) {
return dynamic_cast<TagLib::APE::File*>(file) != nullptr;
}
} // namespace taglib_shim

View file

@ -15,29 +15,15 @@
namespace taglib_shim {
// FileRef helper functions
bool FileRef_isNull(const TagLib::FileRef& ref);
const TagLib::File& FileRef_file(const TagLib::FileRef& ref);
// File tag methods
bool File_tag(const TagLib::File& file);
const TagLib::String& File_tag_title(const TagLib::File& file);
// File type checking functions
bool File_isMPEG(const TagLib::File& file);
bool File_isFLAC(const TagLib::File& file);
bool File_isMP4(const TagLib::File& file);
bool File_isOgg(const TagLib::File& file);
bool File_isOpus(const TagLib::File& file);
bool File_isWAV(const TagLib::File& file);
bool File_isWavPack(const TagLib::File& file);
bool File_isAPE(const TagLib::File& file);
bool File_isMPEG(TagLib::File* file);
bool File_isFLAC(TagLib::File* file);
bool File_isMP4(TagLib::File* file);
bool File_isOgg(TagLib::File* file);
bool File_isOpus(TagLib::File* file);
bool File_isWAV(TagLib::File* file);
bool File_isWavPack(TagLib::File* file);
bool File_isAPE(TagLib::File* file);
// Audio Properties methods
const TagLib::AudioProperties* File_audioProperties(const TagLib::File& file);
int AudioProperties_length(const TagLib::AudioProperties* properties);
int AudioProperties_bitrate(const TagLib::AudioProperties* properties);
int AudioProperties_sampleRate(const TagLib::AudioProperties* properties);
int AudioProperties_channels(const TagLib::AudioProperties* properties);
} // namespace taglib_shim

View file

@ -1,13 +0,0 @@
#include "string_shim.hpp"
namespace taglib_shim {
const char* to_string(const TagLib::String& str) {
return str.toCString(true);
}
bool isEmpty(const TagLib::String& str) {
return str.isEmpty();
}
} // namespace taglib_shim

View file

@ -1,11 +0,0 @@
#pragma once
#include <taglib/tstring.h>
namespace taglib_shim {
// String utilities
const char* to_string(const TagLib::String& str);
bool isEmpty(const TagLib::String& str);
} // namespace taglib_shim

View file

@ -5,8 +5,6 @@ pub(crate) mod bindings {
include!("taglib/tstring.h");
include!("shim/iostream_shim.hpp");
include!("shim/file_shim.hpp");
include!("shim/string_shim.hpp");
include!("shim/audioproperties_shim.hpp");
#[namespace = "TagLib"]
type FileRef;
@ -30,49 +28,39 @@ pub(crate) mod bindings {
fn new_FileRef_from_stream(stream: UniquePtr<RustIOStream>) -> UniquePtr<FileRef>;
// FileRef helper functions
#[namespace = "taglib_shim"]
fn FileRef_isNull(ref_: &FileRef) -> bool;
#[namespace = "taglib_shim"]
fn FileRef_file(ref_: &FileRef) -> &File;
fn isNull(self: Pin<&FileRef>) -> bool;
fn file(self: Pin<&FileRef>) -> *mut File;
// File tag methods
#[namespace = "taglib_shim"]
fn File_tag_title(file: &File) -> &TagString;
fn audioProperties(self: Pin<&File>) -> *mut AudioProperties;
// File type checking functions
#[namespace = "taglib_shim"]
fn File_isMPEG(file: &File) -> bool;
unsafe fn File_isMPEG(file: *mut File) -> bool;
#[namespace = "taglib_shim"]
fn File_isFLAC(file: &File) -> bool;
unsafe fn File_isFLAC(file: *mut File) -> bool;
#[namespace = "taglib_shim"]
fn File_isMP4(file: &File) -> bool;
unsafe fn File_isMP4(file: *mut File) -> bool;
#[namespace = "taglib_shim"]
fn File_isOgg(file: &File) -> bool;
unsafe fn File_isOgg(file: *mut File) -> bool;
#[namespace = "taglib_shim"]
fn File_isOpus(file: &File) -> bool;
unsafe fn File_isOpus(file: *mut File) -> bool;
#[namespace = "taglib_shim"]
fn File_isWAV(file: &File) -> bool;
unsafe fn File_isWAV(file: *mut File) -> bool;
#[namespace = "taglib_shim"]
fn File_isWavPack(file: &File) -> bool;
unsafe fn File_isWavPack(file: *mut File) -> bool;
#[namespace = "taglib_shim"]
fn File_isAPE(file: &File) -> bool;
unsafe fn File_isAPE(file: *mut File) -> bool;
// Audio Properties methods
#[namespace = "taglib_shim"]
unsafe fn File_audioProperties(file: &File) -> *const AudioProperties;
#[namespace = "taglib_shim"]
unsafe fn AudioProperties_lengthInMilliseconds(properties: *const AudioProperties) -> i32;
#[namespace = "taglib_shim"]
unsafe fn AudioProperties_bitrateInKilobitsPerSecond(properties: *const AudioProperties) -> i32;
#[namespace = "taglib_shim"]
unsafe fn AudioProperties_sampleRateInHz(properties: *const AudioProperties) -> i32;
#[namespace = "taglib_shim"]
unsafe fn AudioProperties_numberOfChannels(properties: *const AudioProperties) -> i32;
// AudioProperties methods
fn lengthInMilliseconds(self: Pin<&AudioProperties>) -> i32;
fn bitrate(self: Pin<&AudioProperties>) -> i32;
fn sampleRate(self: Pin<&AudioProperties>) -> i32;
fn channels(self: Pin<&AudioProperties>) -> i32;
// String conversion utilities
#[namespace = "taglib_shim"]
unsafe fn to_string(s: &TagString) -> *const c_char;
unsafe fn toCString(self: Pin<&TagString>, unicode: bool) -> *const c_char;
#[namespace = "taglib_shim"]
fn isEmpty(s: &TagString) -> bool;
fn isEmpty(self: Pin<&TagString>) -> bool;
}
}

View file

@ -1,6 +1,9 @@
mod ffi;
mod stream;
use std::pin::{pin, Pin};
use cxx::UniquePtr;
pub use stream::{RustStream, TagLibStream};
use ffi::bindings;
@ -15,39 +18,30 @@ pub struct AudioProperties {
pub enum File {
Unknown {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
MP3 {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
FLAC {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
MP4 {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
OGG {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
Opus {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
WAV {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
WavPack {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
APE {
title: Option<String>,
audio_properties: Option<AudioProperties>,
},
}
@ -55,28 +49,12 @@ pub enum File {
impl Default for File {
fn default() -> Self {
File::Unknown {
title: None,
audio_properties: None,
}
}
}
impl File {
/// Get the title of the file, if available
pub fn title(&self) -> Option<&str> {
match self {
File::Unknown { title, .. } |
File::MP3 { title, .. } |
File::FLAC { title, .. } |
File::MP4 { title, .. } |
File::OGG { title, .. } |
File::Opus { title, .. } |
File::WAV { title, .. } |
File::WavPack { title, .. } |
File::APE { title, .. } => title.as_deref()
}
}
/// Get the audio properties of the file, if available
pub fn audio_properties(&self) -> Option<&AudioProperties> {
match self {
@ -111,38 +89,26 @@ impl FileRef {
let iostream = unsafe { ffi::bindings::new_rust_iostream(raw_stream) };
// Create FileRef from iostream
let inner = ffi::bindings::new_FileRef_from_stream(iostream);
if ffi::bindings::FileRef_isNull(&inner) {
let file_ref = ffi::bindings::new_FileRef_from_stream(iostream);
if file_ref.is_null() {
return None;
}
// Extract data from C++ objects
let file_ref = &inner;
let file_ptr = ffi::bindings::FileRef_file(&file_ref);
// Extract title
let title = {
let title = ffi::bindings::File_tag_title(file_ptr);
if ffi::bindings::isEmpty(title) {
None
} else {
let cstr = unsafe { ffi::bindings::to_string(title) };
unsafe { std::ffi::CStr::from_ptr(cstr) }
.to_str()
.ok()
.map(|s| s.to_owned())
}
};
let pinned_file_ref = unsafe { Pin::new_unchecked(file_ref.as_ref().unwrap()) };
let file_ptr = pinned_file_ref.file();
// Extract audio properties
let audio_properties = unsafe {
let props_ptr = ffi::bindings::File_audioProperties(file_ptr);
let audio_properties = {
let pinned_file = unsafe { Pin::new_unchecked(&*file_ptr) };
let props_ptr = pinned_file.audioProperties();
if !props_ptr.is_null() {
let props = unsafe { Pin::new_unchecked(&*props_ptr) };
Some(AudioProperties {
length_in_milliseconds: ffi::bindings::AudioProperties_lengthInMilliseconds(props_ptr),
bitrate_in_kilobits_per_second: ffi::bindings::AudioProperties_bitrateInKilobitsPerSecond(props_ptr),
sample_rate_in_hz: ffi::bindings::AudioProperties_sampleRateInHz(props_ptr),
number_of_channels: ffi::bindings::AudioProperties_numberOfChannels(props_ptr),
length_in_milliseconds: props.lengthInMilliseconds(),
bitrate_in_kilobits_per_second: props.bitrate(),
sample_rate_in_hz: props.sampleRate(),
number_of_channels: props.channels(),
})
} else {
None
@ -150,30 +116,30 @@ impl FileRef {
};
// Determine file type and create appropriate variant
let file = {
let file = unsafe {
if ffi::bindings::File_isMPEG(file_ptr) {
File::MP3 { title, audio_properties }
File::MP3 { audio_properties }
} else if ffi::bindings::File_isFLAC(file_ptr) {
File::FLAC { title, audio_properties }
File::FLAC { audio_properties }
} else if ffi::bindings::File_isMP4(file_ptr) {
File::MP4 { title, audio_properties }
File::MP4 { audio_properties }
} else if ffi::bindings::File_isOpus(file_ptr) {
File::Opus { title, audio_properties }
File::Opus { audio_properties }
} else if ffi::bindings::File_isOgg(file_ptr) {
File::OGG { title, audio_properties }
File::OGG { audio_properties }
} else if ffi::bindings::File_isWAV(file_ptr) {
File::WAV { title, audio_properties }
File::WAV { audio_properties }
} else if ffi::bindings::File_isWavPack(file_ptr) {
File::WavPack { title, audio_properties }
File::WavPack { audio_properties }
} else if ffi::bindings::File_isAPE(file_ptr) {
File::APE { title, audio_properties }
File::APE { audio_properties }
} else {
File::Unknown { title, audio_properties }
File::Unknown { audio_properties }
}
};
// Clean up C++ objects - they will be dropped when inner is dropped
drop(inner);
// Clean up C++ objects - they will be dropped when file_ref is dropped
drop(file_ref);
Some(FileRef { file })
}