1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
use enums::{ChannelData, FileTypes, MessageType, Status};
use rtaudio::{CsAudioDevice, RtAudioParams};

/// Struct containing the relevant info of files are opened by csound.
#[derive(Debug, Clone)]
pub struct FileInfo {
    /// pathname of the file; either full or relative to current dir
    pub name: Option<String>,
    /// Enum equivalent code for the file type code from the enum CSOUND_FILETYPES
    pub file_type: FileTypes,
    /// true if Csound is writing the file, false if reading
    pub is_writing: bool,
    /// true if  it is a temporary file that Csound will delete; false if not
    pub is_temp: bool,
}

#[doc(hidden)]
#[derive(Default)]
pub struct Callbacks<'a> {
    pub message_cb: Option<Box<dyn FnMut(MessageType, &str) + 'a>>,
    pub audio_dev_list_cb: Option<Box<dyn FnMut(CsAudioDevice) + 'a>>,
    pub play_open_cb: Option<Box<dyn FnMut(&RtAudioParams) -> Status + 'a>>,
    pub rec_open_cb: Option<Box<dyn FnMut(&RtAudioParams) -> Status + 'a>>,
    pub rt_play_cb: Option<Box<dyn FnMut(&[f64]) + 'a>>,
    pub rt_rec_cb: Option<Box<dyn FnMut(&mut [f64]) -> usize + 'a>>,
    pub sense_event_cb: Option<Box<dyn FnMut() + 'a>>,
    pub keyboard_cb: Option<Box<dyn FnMut() -> char + 'a>>, // TODO this callback doesn't work at the
    //csound side
    pub rt_close_cb: Option<Box<dyn FnMut() + 'a>>,
    pub cscore_cb: Option<Box<dyn FnMut() + 'a>>,
    pub input_channel_cb: Option<Box<dyn FnMut(&str) -> ChannelData + 'a>>,
    pub output_channel_cb: Option<Box<dyn FnMut(&str, ChannelData) + 'a>>,
    pub file_open_cb: Option<Box<dyn FnMut(&FileInfo) + 'a>>,
    pub midi_in_open_cb: Option<Box<dyn FnMut(&str) + 'a>>,
    pub midi_out_open_cb: Option<Box<dyn FnMut(&str) + 'a>>,
    pub midi_read_cb: Option<Box<dyn FnMut(&mut [u8]) -> usize + 'a>>,
    pub midi_write_cb: Option<Box<dyn FnMut(&[u8]) -> usize + 'a>>,
    pub midi_in_close_cb: Option<Box<dyn FnMut() + 'a>>,
    pub midi_out_close_cb: Option<Box<dyn FnMut() + 'a>>,
    pub yield_cb: Option<Box<dyn FnMut() -> bool + 'a>>,
}

pub const MESSAGE_CB: u32 = 1;
pub const SENSE_EVENT: u32 = 2;
pub const PLAY_OPEN: u32 = 3;
pub const REC_OPEN: u32 = 4;
pub const REAL_TIME_PLAY: u32 = 6;
pub const REAL_TIME_REC: u32 = 7;
pub const AUDIO_DEV_LIST: u32 = 9;
//pub const KEYBOARD_CB: u32 = 10;
pub const RT_CLOSE_CB: u32 = 11;
pub const CSCORE_CB: u32 = 12;
pub const CHANNEL_INPUT_CB: u32 = 13;
pub const CHANNEL_OUTPUT_CB: u32 = 14;
pub const FILE_OPEN_CB: u32 = 15;
pub const MIDI_IN_OPEN_CB: u32 = 16;
pub const MIDI_OUT_OPEN_CB: u32 = 17;
pub const MIDI_READ_CB: u32 = 18;
pub const MIDI_WRITE_CB: u32 = 19;
pub const MIDI_IN_CLOSE: u32 = 20;
pub const MIDI_OUT_CLOSE: u32 = 21;
pub const YIELD_CB: u32 = 22;

pub mod Trampoline {

    use std::panic::{self, AssertUnwindSafe};
    pub extern crate csound_sys as raw;
    use super::*;
    use csound::CallbackHandler;
    use libc::{c_char, c_int, c_uchar, /*c_uint,*/ c_void, memcpy};
    use rtaudio::{CsAudioDevice, RtAudioParams};
    use std::ffi::{CStr, CString};
    use std::slice;

    pub fn ptr_to_string(ptr: *const c_char) -> Option<String> {
        let mut result = None;
        if !ptr.is_null() {
            result = match unsafe { CStr::from_ptr(ptr) }.to_str().ok() {
                Some(str_slice) => Some(str_slice.to_owned()),
                None => None,
            };
        }
        result
    }

    pub fn convert_str_to_c<'a, T>(string: T) -> Result<CString, &'static str>
        where
            T: AsRef<str>,
    {
        let string = string.as_ref();
        if string.is_empty() {
            return Err("Empty string");
        }
        CString::new(string).map_err(|_| "Bad string")
    }

    fn catch<T, F: FnOnce() -> T>(f: F) -> Option<T> {
        match panic::catch_unwind(AssertUnwindSafe(f)) {
            Ok(ret) => Some(ret),
            Err(_) => {
                std::process::exit(-1);
            }
        }
    }

    pub extern "C" fn message_string_cb(
        csound: *mut raw::CSOUND,
        attr: c_int,
        message: *const c_char,
    ) {
        catch(|| unsafe {
            let info = CStr::from_ptr(message);
            if let Ok(s) = info.to_str() {
                if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                    .callbacks
                    .message_cb
                    .as_mut()
                {
                    fun(MessageType::from(attr as u32), s);
                }
            }
        });
    }

    /****** Event callbacks functions *******************************************************************/

    pub extern "C" fn senseEventCallback(csound: *mut raw::CSOUND, _userData: *mut c_void) {
        catch(|| unsafe {
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .sense_event_cb
                .as_mut()
            {
                fun();
            }
        });
    }

    /****** real time audio callbacks functions *******************************************************************/

    pub extern "C" fn playOpenCallback(
        csound: *mut raw::CSOUND,
        dev: *const raw::csRtAudioParams,
    ) -> c_int {
        catch(|| unsafe {
            let rtParams = RtAudioParams {
                devName: ptr_to_string((*dev).devName),
                devNum: (*dev).devNum as u32,
                bufSamp_SW: (*dev).bufSamp_SW as u32,
                bufSamp_HW: (*dev).bufSamp_HW as u32,
                nChannels: (*dev).nChannels as u32,
                sampleFormat: (*dev).sampleFormat as u32,
                sampleRate: (*dev).sampleRate as f32,
            };
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .play_open_cb
                .as_mut()
            {
                return fun(&rtParams).to_i32() as c_int;
            }
            0
        })
        .unwrap()
    }

    pub extern "C" fn recOpenCallback(
        csound: *mut raw::CSOUND,
        dev: *const raw::csRtAudioParams,
    ) -> c_int {
        catch(|| unsafe {
            let rtParams = RtAudioParams {
                devName: ptr_to_string((*dev).devName),
                devNum: (*dev).devNum as u32,
                bufSamp_SW: (*dev).bufSamp_SW as u32,
                bufSamp_HW: (*dev).bufSamp_HW as u32,
                nChannels: (*dev).nChannels as u32,
                sampleFormat: (*dev).sampleFormat as u32,
                sampleRate: (*dev).sampleRate as f32,
            };
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .rec_open_cb
                .as_mut()
            {
                return fun(&rtParams).to_i32() as c_int;
            }
            -1
        })
        .unwrap()
    }

    pub extern "C" fn rtcloseCallback(csound: *mut raw::CSOUND) {
        catch(|| unsafe {
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .rt_close_cb
                .as_mut()
            {
                fun();
            }
        });
    }

    pub extern "C" fn rtplayCallback(csound: *mut raw::CSOUND, outBuf: *const f64, nbytes: c_int) {
        catch(|| unsafe {
            let out = slice::from_raw_parts(outBuf, nbytes as usize);
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .rt_play_cb
                .as_mut()
            {
                fun(&out);
            }
        });
    }

    pub extern "C" fn rtrecordCallback(
        csound: *mut raw::CSOUND,
        outBuf: *mut f64,
        nbytes: c_int,
    ) -> c_int {
        catch(|| unsafe {
            let mut buff = slice::from_raw_parts_mut(outBuf, nbytes as usize);
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .rt_rec_cb
                .as_mut()
            {
                return fun(&mut buff) as c_int;
            }
            -1
        })
        .unwrap()
    }

    pub extern "C" fn audioDeviceListCallback(
        csound: *mut raw::CSOUND,
        dev: *mut raw::CS_AUDIODEVICE,
        isOutput: c_int,
    ) -> c_int {
        catch(|| unsafe {
            let audioDevice = CsAudioDevice {
                device_name: ptr_to_string((*dev).device_name.as_ptr()),
                device_id: ptr_to_string((*dev).device_id.as_ptr()),
                rt_module: ptr_to_string((*dev).rt_module.as_ptr()),
                max_nchnls: (*dev).max_nchnls as u32,
                isOutput: isOutput as u32,
            };
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .audio_dev_list_cb
                .as_mut()
            {
                fun(audioDevice);
            }
            0
        })
        .unwrap()
    }

    /*pub extern "C" fn keyboard_callback(
        userData: *mut c_void,
        p: *mut c_void,
        _type_: c_uint,
    ) -> c_int {
        unsafe {
            match (*(userData as *mut CallbackHandler))
                .callbacks
                .keyboard_cb() {
                '\0' => {}
                value => {
                    *(p as *mut c_int) = value as c_int;
                }
            }
            0
        }
    }*/

    /********* General Input/Output callbacks ********************************************************************/
    pub extern "C" fn fileOpenCallback(
        csound: *mut raw::CSOUND,
        filePath: *const c_char,
        fileType: c_int,
        operation: c_int,
        isTemp: c_int,
    ) {
        catch(|| unsafe {
            let name = ptr_to_string(filePath);
            let file_info = FileInfo {
                name,
                file_type: FileTypes::from(fileType as u8),
                is_writing: operation != 0,
                is_temp: isTemp != 0,
            };
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .file_open_cb
                .as_mut()
            {
                fun(&file_info);
            }
        });
    }

    /* Score Handling callbacks ********************************************************* */

    // Sets an pub external callback for Cscore processing. Pass NULL to reset to the internal cscore() function (which does nothing).
    // This callback is retained after a csoundReset() call.
    pub extern "C" fn scoreCallback(csound: *mut raw::CSOUND) {
        catch(|| unsafe {
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .cscore_cb
                .as_mut()
            {
                fun();
            }
        });
    }

    /* Channels and events callbacks **************************************************** */

    pub extern "C" fn inputChannelCallback(
        csound: *mut raw::CSOUND,
        channelName: *const c_char,
        channelValuePtr: *mut c_void,
        _channelType: *const c_void,
    ) {
        catch(|| unsafe {
            let name = (CStr::from_ptr(channelName)).to_str();
            if name.is_err() {
                return;
            }
            let name = name.unwrap();
            let result = if let Some(fun) = (*(raw::csoundGetHostData(csound)
                as *mut CallbackHandler))
                .callbacks
                .input_channel_cb
                .as_mut()
            {
                fun(name)
            } else {
                return;
            };

            match result {
                ChannelData::CS_CONTROL_CHANNEL(data) => {
                    *(channelValuePtr as *mut f64) = data;
                }

                ChannelData::CS_STRING_CHANNEL(s) => {
                    let len = s.len();
                    let c_str = CString::new(s);
                    if raw::csoundGetChannelDatasize(csound, channelName) as usize <= len
                        && c_str.is_ok()
                    {
                        memcpy(channelValuePtr, c_str.unwrap().as_ptr() as *mut c_void, len);
                    }
                }

                _ => {}
            }
        });
    }

    pub extern "C" fn outputChannelCallback(
        csound: *mut raw::CSOUND,
        channelName: *const c_char,
        channelValuePtr: *mut c_void,
        _channelType: *const c_void,
    ) {
        catch(|| unsafe {
            let name = (CStr::from_ptr(channelName)).to_str();
            if name.is_err() {
                return;
            }
            let name = name.unwrap();
            let mut ptr = ::std::ptr::null_mut();
            let ptr: *mut *mut f64 = &mut ptr as *mut *mut _;
            let channel_type = raw::csoundGetChannelPtr(csound, ptr, channelName, 0) as u32;
            let channel_type = channel_type & raw::CSOUND_CHANNEL_TYPE_MASK as u32;

            let fun = if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .output_channel_cb
                .as_mut()
            {
                fun
            } else {
                return;
            };

            match channel_type {
                raw::CSOUND_CONTROL_CHANNEL => {
                    let value = *(channelValuePtr as *mut f64);
                    let data = ChannelData::CS_CONTROL_CHANNEL(value);
                    fun(name, data);
                }

                raw::CSOUND_STRING_CHANNEL => {
                    let data = ChannelData::CS_STRING_CHANNEL(
                        ptr_to_string(channelValuePtr as *const c_char)
                            .unwrap_or_else(|| "".to_owned()),
                    );
                    fun(name, data);
                }

                _ => {}
            }
        });
    }

    /****** MIDI I/O callbacks functions *******************************************************************/

    // Sets callback for opening real time MIDI input.
    pub extern "C" fn midiInOpenCallback(
        csound: *mut raw::CSOUND,
        _userData: *mut *mut c_void,
        devName: *const c_char,
    ) -> c_int {
        catch(|| unsafe {
            let name = match CStr::from_ptr(devName).to_str() {
                Ok(s) => s,
                _ => return raw::CSOUND_ERROR,
            };
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .midi_in_open_cb
                .as_mut()
            {
                fun(&name);
            }
            raw::CSOUND_SUCCESS
        })
        .unwrap()
    }

    // Sets callback for opening real time MIDI output.
    pub extern "C" fn midiOutOpenCallback(
        csound: *mut raw::CSOUND,
        _userData: *mut *mut c_void,
        devName: *const c_char,
    ) -> c_int {
        catch(|| unsafe {
            let name = match CStr::from_ptr(devName).to_str() {
                Ok(s) => s,
                _ => return raw::CSOUND_ERROR,
            };
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .midi_out_open_cb
                .as_mut()
            {
                fun(&name);
            }
            raw::CSOUND_SUCCESS
        })
        .unwrap()
    }

    // Sets callback for reading from real time MIDI input.
    pub extern "C" fn midiReadCallback(
        csound: *mut raw::CSOUND,
        _userData: *mut c_void,
        buf: *mut c_uchar,
        nbytes: c_int,
    ) -> c_int {
        catch(|| unsafe {
            let mut out = slice::from_raw_parts_mut(buf, nbytes as usize);
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .midi_read_cb
                .as_mut()
            {
                return fun(&mut out) as c_int;
            }
            -1
        })
        .unwrap()
    }

    // Sets callback for writing to real time MIDI output.
    #[allow(dead_code)]
    pub extern "C" fn midiWriteCallback(
        csound: *mut raw::CSOUND,
        _userData: *mut c_void,
        buf: *const u8,
        nbytes: c_int,
    ) -> c_int {
        catch(|| unsafe {
            let buffer = slice::from_raw_parts(buf, nbytes as usize);
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .midi_write_cb
                .as_mut()
            {
                return fun(&buffer) as c_int;
            }
            -1
        })
        .unwrap()
    }

    //Sets callback for closing real time MIDI input.
    pub extern "C" fn midiInCloseCallback(
        csound: *mut raw::CSOUND,
        _userData: *mut c_void,
    ) -> c_int {
        catch(|| unsafe {
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .midi_in_close_cb
                .as_mut()
            {
                fun();
            }
            raw::CSOUND_SUCCESS
        })
        .unwrap()
    }

    // Sets callback for closing real time MIDI output.
    pub extern "C" fn midiOutCloseCallback(
        csound: *mut raw::CSOUND,
        _userData: *mut c_void,
    ) -> c_int {
        catch(|| unsafe {
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .midi_out_close_cb
                .as_mut()
            {
                fun();
            }
            raw::CSOUND_SUCCESS
        })
        .unwrap()
    }

    pub extern "C" fn yieldCallback(csound: *mut raw::CSOUND) -> c_int {
        catch(|| unsafe {
            if let Some(fun) = (*(raw::csoundGetHostData(csound) as *mut CallbackHandler))
                .callbacks
                .yield_cb
                .as_mut()
            {
                return fun() as c_int;
            }
            0
        })
        .unwrap()
    }

}

//Sets callback for converting MIDI error codes to strings.
/*pub extern fn pub externalMidiErrorStringCallback (midi_error_code : c_int) -> *const c_char {
    unsafe{
    }
}*/