sdif_sys/lib.rs
1//! # sdif-sys
2//!
3//! Raw FFI bindings to the IRCAM SDIF (Sound Description Interchange Format) library.
4//!
5//! This crate provides low-level, unsafe bindings to the SDIF C library. For a safe,
6//! idiomatic Rust API, use the `sdif-rs` crate instead.
7//!
8//! ## Usage
9//!
10//! These bindings are primarily intended for use by the `sdif-rs` crate. Direct usage
11//! requires careful attention to:
12//!
13//! - Calling `SdifGenInit` before any other SDIF functions
14//! - Calling `SdifGenKill` during cleanup
15//! - Managing `SdifFileT` pointer lifetimes
16//! - Following the correct sequence of read/write operations
17//!
18//! ## Example
19//!
20//! ```no_run
21//! use sdif_sys::*;
22//! use std::ptr;
23//! use std::ffi::CString;
24//!
25//! unsafe {
26//! // Initialize the library (required before any operations)
27//! SdifGenInit(ptr::null());
28//!
29//! // Open a file for reading
30//! let path = CString::new("test.sdif").unwrap();
31//! let file = SdifFOpen(path.as_ptr(), SdifFileModeET_eReadFile);
32//!
33//! if !file.is_null() {
34//! // Read general header
35//! let bytes_read = SdifFReadGeneralHeader(file);
36//!
37//! // Read ASCII chunks (NVT, type definitions, etc.)
38//! let ascii_bytes = SdifFReadAllASCIIChunks(file);
39//!
40//! // Close the file
41//! SdifFClose(file);
42//! }
43//!
44//! // Cleanup
45//! SdifGenKill();
46//! }
47//! ```
48//!
49//! ## Feature Flags
50//!
51//! - `bundled`: Compile SDIF from bundled source instead of linking to system library
52//! - `static`: Force static linking (implies `bundled` on most systems)
53
54#![allow(non_upper_case_globals)]
55#![allow(non_camel_case_types)]
56#![allow(non_snake_case)]
57#![allow(dead_code)]
58#![allow(clippy::all)]
59
60// Include the generated bindings
61include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
62
63// ============================================================================
64// Additional Constants and Type Aliases
65// ============================================================================
66//
67// Note: The bindgen-generated bindings automatically create type aliases like:
68// - SdifFileModeE as SdifFileModeET
69// - SdifDataTypeE as SdifDataTypeET
70// These provide compatibility with code expecting the "ET" suffix naming.
71//
72// For the enum constants, we need to provide aliases manually since bindgen
73// creates the type aliases but not the constant aliases.
74
75#[cfg(not(sdif_stub_bindings))]
76pub use SdifFileModeE_eReadFile as SdifFileModeET_eReadFile;
77#[cfg(not(sdif_stub_bindings))]
78pub use SdifFileModeE_eWriteFile as SdifFileModeET_eWriteFile;
79
80#[cfg(not(sdif_stub_bindings))]
81pub use SdifDataTypeE_eFloat4 as SdifDataTypeET_eFloat4;
82#[cfg(not(sdif_stub_bindings))]
83pub use SdifDataTypeE_eFloat8 as SdifDataTypeET_eFloat8;
84#[cfg(not(sdif_stub_bindings))]
85pub use SdifDataTypeE_eInt1 as SdifDataTypeET_eInt1;
86#[cfg(not(sdif_stub_bindings))]
87pub use SdifDataTypeE_eInt2 as SdifDataTypeET_eInt2;
88#[cfg(not(sdif_stub_bindings))]
89pub use SdifDataTypeE_eInt4 as SdifDataTypeET_eInt4;
90#[cfg(not(sdif_stub_bindings))]
91pub use SdifDataTypeE_eText as SdifDataTypeET_eText;
92#[cfg(not(sdif_stub_bindings))]
93pub use SdifDataTypeE_eUInt1 as SdifDataTypeET_eUInt1;
94#[cfg(not(sdif_stub_bindings))]
95pub use SdifDataTypeE_eUInt2 as SdifDataTypeET_eUInt2;
96#[cfg(not(sdif_stub_bindings))]
97pub use SdifDataTypeE_eUInt4 as SdifDataTypeET_eUInt4;
98
99// Note: SdifSignature type is defined in the generated bindings
100
101/// Convert a 4-character string to an SDIF signature.
102///
103/// This function uses bit manipulation to create the signature without calling the C library.
104/// For compatibility with the SDIF library, you can also use `SdifStringToSignature`.
105///
106/// # Panics
107///
108/// Panics if the string is not exactly 4 bytes.
109pub fn signature_from_str(s: &str) -> SdifSignature {
110 assert_eq!(s.len(), 4, "SDIF signatures must be exactly 4 characters");
111 let bytes = s.as_bytes();
112 ((bytes[0] as u32) << 24)
113 | ((bytes[1] as u32) << 16)
114 | ((bytes[2] as u32) << 8)
115 | (bytes[3] as u32)
116}
117
118/// Convert an SDIF signature to a 4-character string.
119///
120/// This function uses bit manipulation to decode the signature without calling the C library.
121/// For compatibility with the SDIF library, you can also use `SdifSignatureToString`.
122pub fn signature_to_string(sig: SdifSignature) -> String {
123 let bytes = [
124 ((sig >> 24) & 0xFF) as u8,
125 ((sig >> 16) & 0xFF) as u8,
126 ((sig >> 8) & 0xFF) as u8,
127 (sig & 0xFF) as u8,
128 ];
129 String::from_utf8_lossy(&bytes).into_owned()
130}
131
132/// Convert a 4-character string to an SDIF signature using the C library.
133///
134/// This is a safe wrapper around `SdifStringToSignature`.
135/// When not using stub bindings, this calls the actual SDIF library function.
136///
137/// # Panics
138///
139/// Panics if the string is not exactly 4 bytes.
140#[cfg(not(sdif_stub_bindings))]
141pub fn string_to_signature_c(s: &str) -> SdifSignature {
142 use std::ffi::CString;
143 assert_eq!(s.len(), 4, "SDIF signatures must be exactly 4 characters");
144 let c_str = CString::new(s).expect("String contains null byte");
145 unsafe { SdifStringToSignature(c_str.as_ptr()) }
146}
147
148// ============================================================================
149// Common Frame Type Signatures
150// ============================================================================
151
152/// 1TRC - Sinusoidal Tracks (most common for additive synthesis)
153pub const SIG_1TRC: SdifSignature = signature_from_str_const(b"1TRC");
154
155/// 1HRM - Harmonic Partials
156pub const SIG_1HRM: SdifSignature = signature_from_str_const(b"1HRM");
157
158/// 1FQ0 - Fundamental Frequency
159pub const SIG_1FQ0: SdifSignature = signature_from_str_const(b"1FQ0");
160
161/// 1RES - Resonances
162pub const SIG_1RES: SdifSignature = signature_from_str_const(b"1RES");
163
164/// 1STF - Short-Time Fourier
165pub const SIG_1STF: SdifSignature = signature_from_str_const(b"1STF");
166
167/// Convert a 4-byte array to signature at compile time
168const fn signature_from_str_const(s: &[u8; 4]) -> SdifSignature {
169 ((s[0] as u32) << 24) | ((s[1] as u32) << 16) | ((s[2] as u32) << 8) | (s[3] as u32)
170}
171
172// ============================================================================
173// Tests
174// ============================================================================
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use std::ptr;
180
181 #[test]
182 fn test_signature_conversion() {
183 assert_eq!(signature_from_str("1TRC"), SIG_1TRC);
184 assert_eq!(signature_to_string(SIG_1TRC), "1TRC");
185
186 assert_eq!(signature_from_str("1HRM"), SIG_1HRM);
187 assert_eq!(signature_to_string(SIG_1HRM), "1HRM");
188
189 // Roundtrip test
190 let sig = signature_from_str("TEST");
191 assert_eq!(signature_to_string(sig), "TEST");
192 }
193
194 #[test]
195 #[should_panic(expected = "SDIF signatures must be exactly 4 characters")]
196 fn test_signature_wrong_length() {
197 signature_from_str("TOO_LONG");
198 }
199
200 // Tests that call SDIF functions are only available when NOT using stub bindings
201 #[test]
202 #[cfg(not(sdif_stub_bindings))]
203 fn test_init_and_kill() {
204 // This test verifies that the C library can be initialized and cleaned up
205 // without crashing. It's a basic smoke test.
206 unsafe {
207 SdifGenInit(ptr::null());
208 SdifGenKill();
209 }
210 }
211
212 #[test]
213 fn test_file_mode_constants() {
214 // Verify the file mode constants exist and have expected values
215 // These are typically defined as 1 and 2 in the SDIF library
216 assert!(SdifFileModeET_eReadFile != SdifFileModeET_eWriteFile);
217 }
218
219 #[test]
220 #[cfg(not(sdif_stub_bindings))]
221 fn test_data_type_sizes() {
222 // Verify data type constants match expected sizes
223 // eFloat4 = 4 bytes (f32), eFloat8 = 8 bytes (f64)
224 // Note: The actual values may vary by SDIF version; adjust as needed
225 unsafe {
226 let size_f4 = SdifSizeofDataType(SdifDataTypeET_eFloat4);
227 let size_f8 = SdifSizeofDataType(SdifDataTypeET_eFloat8);
228
229 assert_eq!(size_f4, 4, "eFloat4 should be 4 bytes");
230 assert_eq!(size_f8, 8, "eFloat8 should be 8 bytes");
231 }
232 }
233}