Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  lib.rs   Sprache: unbekannt

 
extern crate thin_vec;

use midir::{
    InitError, MidiInput, MidiInputConnection, MidiInputPort, MidiOutput, MidiOutputConnection,
    MidiOutputPort,
};
use nsstring::{nsAString, nsString};
use std::ptr;
use thin_vec::ThinVec;
use uuid::Uuid;

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate midir;

#[cfg(target_os = "windows")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct GeckoTimeStamp {
    gtc: u64,
    qpc: u64,

    is_null: u8,
    has_qpc: u8,
}

#[cfg(not(target_os = "windows"))]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct GeckoTimeStamp {
    value: u64,
}

enum MidiConnection {
    Input(MidiInputConnection<CallbackData>),
    Output(MidiOutputConnection),
}

struct MidiConnectionWrapper {
    id: String,
    connection: MidiConnection,
}

enum MidiPort {
    Input(MidiInputPort),
    Output(MidiOutputPort),
}

struct MidiPortWrapper {
    id: String,
    name: String,
    port: MidiPort,
    open_count: u32,
}

impl MidiPortWrapper {
    fn input(self: &MidiPortWrapper) -> bool {
        match self.port {
            MidiPort::Input(_) => true,
            MidiPort::Output(_) => false,
        }
    }
}

pub struct MidirWrapper {
    ports: Vec<MidiPortWrapper>,
    connections: Vec<MidiConnectionWrapper>,
}

struct CallbackData {
    nsid: nsString,
    open_timestamp: GeckoTimeStamp,
}

type AddCallback = unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool);
type RemoveCallback = AddCallback;

impl MidirWrapper {
    fn refresh(
        self: &mut MidirWrapper,
        add_callback: AddCallback,
        remove_callback: Option<RemoveCallback>,
    ) {
        if let Ok(ports) = collect_ports() {
            if let Some(remove_callback) = remove_callback {
                self.remove_missing_ports(&ports, remove_callback);
            }

            self.add_new_ports(ports, add_callback);
        }
    }

    fn remove_missing_ports(
        self: &mut MidirWrapper,
        ports: &Vec<MidiPortWrapper>,
        remove_callback: RemoveCallback,
    ) {
        let old_ports = &mut self.ports;
        let mut i = 0;
        while i < old_ports.len() {
            if !ports
                .iter()
                .any(|p| p.name == old_ports[i].name && p.input() == old_ports[i].input())
            {
                let port = old_ports.remove(i);
                let id = nsString::from(&port.id);
                let name = nsString::from(&port.name);
                unsafe { remove_callback(&id, &name, port.input()) };
            } else {
                i += 1;
            }
        }
    }

    fn add_new_ports(
        self: &mut MidirWrapper,
        ports: Vec<MidiPortWrapper>,
        add_callback: AddCallback,
    ) {
        for port in ports {
            if !self.is_port_present(&port) && !Self::is_microsoft_synth_output(&port) {
                let id = nsString::from(&port.id);
                let name = nsString::from(&port.name);
                unsafe { add_callback(&id, &name, port.input()) };
                self.ports.push(port);
            }
        }
    }

    fn is_port_present(self: &MidirWrapper, port: &MidiPortWrapper) -> bool {
        self.ports
            .iter()
            .any(|p| p.name == port.name && p.input() == port.input())
    }

    // We explicitly disable Microsoft's soft synthesizer, see bug 1798097
    fn is_microsoft_synth_output(port: &MidiPortWrapper) -> bool {
        (port.input() == false) && (port.name == "Microsoft GS Wavetable Synth")
    }

    fn open_port(
        self: &mut MidirWrapper,
        nsid: &nsString,
        timestamp: GeckoTimeStamp,
        callback: unsafe extern "C" fn(
            id: &nsString,
            data: *const u8,
            length: usize,
            timestamp: &GeckoTimeStamp,
            micros: u64,
        ),
    ) -> Result<(), ()> {
        let id = nsid.to_string();
        let connections = &mut self.connections;
        let port = self.ports.iter_mut().find(|e| e.id.eq(&id));
        if let Some(port) = port {
            if port.open_count == 0 {
                let connection = match &port.port {
                    MidiPort::Input(port) => {
                        let input = MidiInput::new("WebMIDI input").map_err(|_err| ())?;
                        let data = CallbackData {
                            nsid: nsid.clone(),
                            open_timestamp: timestamp,
                        };
                        let connection = input
                            .connect(
                                port,
                                "Input connection",
                                move |stamp, message, data| unsafe {
                                    callback(
                                        &data.nsid,
                                        message.as_ptr(),
                                        message.len(),
                                        &data.open_timestamp,
                                        stamp,
                                    );
                                },
                                data,
                            )
                            .map_err(|_err| ())?;
                        MidiConnectionWrapper {
                            id: id.clone(),
                            connection: MidiConnection::Input(connection),
                        }
                    }
                    MidiPort::Output(port) => {
                        let output = MidiOutput::new("WebMIDI output").map_err(|_err| ())?;
                        let connection = output
                            .connect(port, "Output connection")
                            .map_err(|_err| ())?;
                        MidiConnectionWrapper {
                            connection: MidiConnection::Output(connection),
                            id: id.clone(),
                        }
                    }
                };

                connections.push(connection);
            }

            port.open_count += 1;
            return Ok(());
        }

        Err(())
    }

    fn close_port(self: &mut MidirWrapper, id: &str) {
        let port = self.ports.iter_mut().find(|e| e.id.eq(&id)).unwrap();
        port.open_count -= 1;

        if port.open_count > 0 {
            return;
        }

        let connections = &mut self.connections;
        let index = connections.iter().position(|e| e.id.eq(id)).unwrap();
        let connection_wrapper = connections.remove(index);

        match connection_wrapper.connection {
            MidiConnection::Input(connection) => {
                connection.close();
            }
            MidiConnection::Output(connection) => {
                connection.close();
            }
        }
    }

    fn send(self: &mut MidirWrapper, id: &str, data: &[u8]) -> Result<(), ()> {
        let connections = &mut self.connections;
        let index = connections.iter().position(|e| e.id.eq(id)).ok_or(())?;
        let connection_wrapper = connections.get_mut(index).unwrap();

        match &mut connection_wrapper.connection {
            MidiConnection::Output(connection) => {
                connection.send(data).map_err(|_err| ())?;
            }
            _ => {
                panic!("Sending on an input port!");
            }
        }

        Ok(())
    }
}

fn collect_ports() -> Result<Vec<MidiPortWrapper>, InitError> {
    let input = MidiInput::new("WebMIDI input")?;
    let output = MidiOutput::new("WebMIDI output")?;
    let mut ports = Vec::<MidiPortWrapper>::new();
    collect_input_ports(&input, &mut ports);
    collect_output_ports(&output, &mut ports);
    Ok(ports)
}

impl MidirWrapper {
    fn new() -> Result<MidirWrapper, InitError> {
        let ports = Vec::new();
        let connections: Vec<MidiConnectionWrapper> = Vec::new();
        Ok(MidirWrapper { ports, connections })
    }
}

/// Create the C++ wrapper that will be used to talk with midir.
///
/// This function will be exposed to C++
///
/// # Safety
///
/// This function deliberately leaks the wrapper because ownership is
/// transfered to the C++ code. Use [midir_impl_shutdown()] to free it.
#[no_mangle]
pub unsafe extern "C" fn midir_impl_init(callback: AddCallback) -> *mut MidirWrapper {
    if let Ok(mut midir_impl) = MidirWrapper::new() {
        midir_impl.refresh(callback, None);

        // Gecko invokes this initialization on a separate thread from all the
        // other operations, so make it clear to Rust this needs to be Send.
        fn assert_send<T: Send>(_: &T) {}
        assert_send(&midir_impl);

        let midir_box = Box::new(midir_impl);
        // Leak the object as it will be owned by the C++ code from now on
        Box::leak(midir_box) as *mut _
    } else {
        ptr::null_mut()
    }
}

/// Refresh the list of ports.
///
/// This function will be exposed to C++
///
/// # Safety
///
/// `wrapper` must be the pointer returned by [midir_impl_init()].
#[no_mangle]
pub unsafe extern "C" fn midir_impl_refresh(
    wrapper: *mut MidirWrapper,
    add_callback: AddCallback,
    remove_callback: RemoveCallback,
) {
    (*wrapper).refresh(add_callback, Some(remove_callback))
}

/// Shutdown midir and free the C++ wrapper.
///
/// This function will be exposed to C++
///
/// # Safety
///
/// `wrapper` must be the pointer returned by [midir_impl_init()]. After this
/// has been called the wrapper object will be destoyed and cannot be accessed
/// anymore.
#[no_mangle]
pub unsafe extern "C" fn midir_impl_shutdown(wrapper: *mut MidirWrapper) {
    // The MidirImpl object will be automatically destroyed when the contents
    // of this box are automatically dropped at the end of the function
    let _midir_box = Box::from_raw(wrapper);
}

/// Open a MIDI port.
///
/// This function will be exposed to C++
///
/// # Safety
///
/// `wrapper` must be the pointer returned by [midir_impl_init()].
#[no_mangle]
pub unsafe extern "C" fn midir_impl_open_port(
    wrapper: *mut MidirWrapper,
    nsid: *mut nsString,
    timestamp: *mut GeckoTimeStamp,
    callback: unsafe extern "C" fn(
        id: &nsString,
        data: *const u8,
        length: usize,
        timestamp: &GeckoTimeStamp,
        micros: u64,
    ),
) -> bool {
    (*wrapper)
        .open_port(nsid.as_ref().unwrap(), *timestamp, callback)
        .is_ok()
}

/// Close a MIDI port.
///
/// This function will be exposed to C++
///
/// # Safety
///
/// `wrapper` must be the pointer returned by [midir_impl_init()].
#[no_mangle]
pub unsafe extern "C" fn midir_impl_close_port(wrapper: *mut MidirWrapper, id: *mut nsString) {
    (*wrapper).close_port(&(*id).to_string());
}

/// Send a message over a MIDI output port.
///
/// This function will be exposed to C++
///
/// # Safety
///
/// `wrapper` must be the pointer returned by [midir_impl_init()].
#[no_mangle]
pub unsafe extern "C" fn midir_impl_send(
    wrapper: *mut MidirWrapper,
    id: *const nsAString,
    data: *const ThinVec<u8>,
) -> bool {
    (*wrapper)
        .send(&(*id).to_string(), (*data).as_slice())
        .is_ok()
}

fn collect_input_ports(input: &MidiInput, wrappers: &mut Vec<MidiPortWrapper>) {
    let ports = input.ports();
    for port in ports {
        let id = Uuid::new_v4()
            .as_hyphenated()
            .encode_lower(&mut Uuid::encode_buffer())
            .to_owned();
        let name = input
            .port_name(&port)
            .unwrap_or_else(|_| "unknown input port".to_string());
        let port = MidiPortWrapper {
            id,
            name,
            port: MidiPort::Input(port),
            open_count: 0,
        };
        wrappers.push(port);
    }
}

fn collect_output_ports(output: &MidiOutput, wrappers: &mut Vec<MidiPortWrapper>) {
    let ports = output.ports();
    for port in ports {
        let id = Uuid::new_v4()
            .as_hyphenated()
            .encode_lower(&mut Uuid::encode_buffer())
            .to_owned();
        let name = output
            .port_name(&port)
            .unwrap_or_else(|_| "unknown input port".to_string());
        let port = MidiPortWrapper {
            id,
            name,
            port: MidiPort::Output(port),
            open_count: 0,
        };
        wrappers.push(port);
    }
}

[ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge