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: SdifSignature type is defined in the generated bindings
68
69/// Convert a 4-character string to an SDIF signature.
70///
71/// # Safety
72///
73/// The C function is called internally. The input must be exactly 4 ASCII characters.
74///
75/// # Panics
76///
77/// Panics if the string is not exactly 4 bytes.
78pub fn signature_from_str(s: &str) -> SdifSignature {
79 assert_eq!(s.len(), 4, "SDIF signatures must be exactly 4 characters");
80 let bytes = s.as_bytes();
81 ((bytes[0] as u32) << 24)
82 | ((bytes[1] as u32) << 16)
83 | ((bytes[2] as u32) << 8)
84 | (bytes[3] as u32)
85}
86
87/// Convert an SDIF signature to a 4-character string.
88pub fn signature_to_string(sig: SdifSignature) -> String {
89 let bytes = [
90 ((sig >> 24) & 0xFF) as u8,
91 ((sig >> 16) & 0xFF) as u8,
92 ((sig >> 8) & 0xFF) as u8,
93 (sig & 0xFF) as u8,
94 ];
95 String::from_utf8_lossy(&bytes).into_owned()
96}
97
98// ============================================================================
99// Common Frame Type Signatures
100// ============================================================================
101
102/// 1TRC - Sinusoidal Tracks (most common for additive synthesis)
103pub const SIG_1TRC: SdifSignature = signature_from_str_const(b"1TRC");
104
105/// 1HRM - Harmonic Partials
106pub const SIG_1HRM: SdifSignature = signature_from_str_const(b"1HRM");
107
108/// 1FQ0 - Fundamental Frequency
109pub const SIG_1FQ0: SdifSignature = signature_from_str_const(b"1FQ0");
110
111/// 1RES - Resonances
112pub const SIG_1RES: SdifSignature = signature_from_str_const(b"1RES");
113
114/// 1STF - Short-Time Fourier
115pub const SIG_1STF: SdifSignature = signature_from_str_const(b"1STF");
116
117/// Convert a 4-byte array to signature at compile time
118const fn signature_from_str_const(s: &[u8; 4]) -> SdifSignature {
119 ((s[0] as u32) << 24)
120 | ((s[1] as u32) << 16)
121 | ((s[2] as u32) << 8)
122 | (s[3] as u32)
123}
124
125// ============================================================================
126// Tests
127// ============================================================================
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use std::ptr;
133
134 #[test]
135 fn test_signature_conversion() {
136 assert_eq!(signature_from_str("1TRC"), SIG_1TRC);
137 assert_eq!(signature_to_string(SIG_1TRC), "1TRC");
138
139 assert_eq!(signature_from_str("1HRM"), SIG_1HRM);
140 assert_eq!(signature_to_string(SIG_1HRM), "1HRM");
141
142 // Roundtrip test
143 let sig = signature_from_str("TEST");
144 assert_eq!(signature_to_string(sig), "TEST");
145 }
146
147 #[test]
148 #[should_panic(expected = "SDIF signatures must be exactly 4 characters")]
149 fn test_signature_wrong_length() {
150 signature_from_str("TOO_LONG");
151 }
152
153 // Tests that call SDIF functions are only available when NOT using stub bindings
154 #[test]
155 #[cfg(not(sdif_stub_bindings))]
156 fn test_init_and_kill() {
157 // This test verifies that the C library can be initialized and cleaned up
158 // without crashing. It's a basic smoke test.
159 unsafe {
160 SdifGenInit(ptr::null());
161 SdifGenKill();
162 }
163 }
164
165 #[test]
166 #[cfg(not(sdif_stub_bindings))]
167 fn test_double_init_is_safe() {
168 // SDIF library should handle multiple init calls gracefully
169 unsafe {
170 SdifGenInit(ptr::null());
171 SdifGenInit(ptr::null());
172 SdifGenKill();
173 SdifGenKill();
174 }
175 }
176
177 #[test]
178 fn test_file_mode_constants() {
179 // Verify the file mode constants exist and have expected values
180 // These are typically defined as 1 and 2 in the SDIF library
181 assert!(SdifFileModeET_eReadFile != SdifFileModeET_eWriteFile);
182 }
183
184 #[test]
185 #[cfg(not(sdif_stub_bindings))]
186 fn test_data_type_sizes() {
187 // Verify data type constants match expected sizes
188 // eFloat4 = 4 bytes (f32), eFloat8 = 8 bytes (f64)
189 // Note: The actual values may vary by SDIF version; adjust as needed
190 unsafe {
191 let size_f4 = SdifSizeofDataType(SdifDataTypeET_eFloat4);
192 let size_f8 = SdifSizeofDataType(SdifDataTypeET_eFloat8);
193
194 assert_eq!(size_f4, 4, "eFloat4 should be 4 bytes");
195 assert_eq!(size_f8, 8, "eFloat8 should be 8 bytes");
196 }
197 }
198}