# nannou_osc | Intro
###### tags: `rust` `nannou` `osc` `tutorial`
#### Part of a series on OSC in Nannou
1. [Introduction](https://hackmd.io/@flowb/SJCsaOsvs)
2. [Sending OSC](https://hackmd.io/@flowb/HkDfjF1Os)
3. [Receiving OSC](https://hackmd.io/@flowb/BJwWBPvOs)
## Aknowledgements
This series of articles builds on the [OSC Turial](https://guide.nannou.cc/tutorials/osc/osc-introduction.html) in the [Nannou Guide](https://guide.nannou.cc/welcome.html) by [madskjeldgaard](https://madskjeldgaard.dk/). Some of the code included herein was copied over or re-worked based on their work.
## Introduction
In this series, we're going to learn how to integrate the [OSC](https://en.wikipedia.org/wiki/Open_Sound_Control) network protocol with a Nannou Application.
### Some Assumptions
Some basic familiarity with Nannou and Rust is assumed. If you're completely new to both, consider starting with the [Nannou Guide](https://guide.nannou.cc). There you can find lots of good jumping off points for more info.
### What is OSC?
OSC stands for "Open Sound Control". It is a network protocol used in live performance and entertainment. Wikipedia has a great summary available [here](https://en.wikipedia.org/wiki/Open_Sound_Control).
To summarize, OSC is a way to share values of different types among multiple application instances using a network connection.
### How does it work with Nannou?
Let's refer back to [Anatomy of a Nannou App](https://guide.nannou.cc/tutorials/basics/anatomy-of-a-nannou-app.html) from the [Guide](https://guide.nannou.cc/) and it's terse explanation of the [Model-View-Controller pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) as it pertains to Nannou:
- a **model** describing the internal state
- a **view** describing how to present the model and
- a **controller** describing how to update the model on certain events
Since OSC is a means to move values around, we're going to use it to read values from the model of one Nannou App and, send them to update the model of another Nannou App.
Networked application development has a lot of possible ways that things can go awry. In general, you'll want to do localized (non-networked) development of an application that expects control input before adding network communications to it. In this way you can make sure that your Model-View-Controller mechanisms are behaving as expected, before adding in something that might fail because of common issues like incorrect IP Addresses, ports or [air-gaps](https://en.wikipedia.org/wiki/Air_gap_(networking)).
In order to speed things along, we're going to use an existing project that already implements the MVC paradigm and extend it with OSC support.
We will make two copies of this project. The first will transmit values from it's model struct via OSC. The second will receive these values and use them to update it's own model struct instance.
## Clock Demo Project
Our test-case is a simple demo project that vaguely behaves like a mechanical clock.
The App has three controls the user can interact with:
- a **rate** value that controls how quickly the hands turn
- a **number of hands** value that changes how many hands are drawn on the clock
- and a **gear ratio** that changes the relationship between the hands
The full project is available [on github](https://github.com/flowb/clock) if you'd like to just clone the whole repo. A full copy of all the project code is also at the bottom of this document.
Once you get it pulled down, open up a terminal go to the project directory and verify that it runs with
```shell
cargo run --release
```
After all the crate downloading and compiling is done, you should see something like this:

Go ahead and play with the sliders to make sure everything is working properly.
### The Clock's Model
If you open up src/main.rs in the project directory in your favorite IDE we can take a look at some of the clockwork.
The code is fairly lean but, even so, pretty much all of our work will be happening within the Model struct and, the *model* and *update* functions.
The Model struct of our project currently looks like this:
```rust=
struct Model {
mainspring: f32,
rate: f32,
num_hands: i32,
gear_ratio: i32,
egui: Egui,
}
```
- **mainspring**: this is the main value from which all the hands derive their position.
- **rate**: controls the speed at which the mainspring is turned.
- **num_hands**: changes how many hands are drawn on our clock.
- **gear_ratio**: the movement relationship between our hands. For example, if this is set to 2 then for every two turns of one hand, the next smaller hand will turn once.
- **egui**: This is a handle for the user interface. We won't need to meddle with this bit.
### What's next?
In the next section we're going to make a copy of this project and modify it to transmit the *mainspring, rate, num_hands* and *gear_ratio* values with OSC.
Next Section: [osc-sender](https://hackmd.io/@flowb/HkDfjF1Os)
---
## Clock project code
If you prefer to set up your own version of the project from scratch, open a terminal and create a new project directory with:
```shell
cargo new clock
```
Then use your favorite IDE and copy/paste the contents for Cargo.toml and src/main.rs into the respective files.
### Cargo.toml
```toml=
[package]
name = "clock"
version = "0.1.0"
edition = "2021"
[dependencies]
nannou = "0.18.0"
nannou_egui = "0.5.0"
```
### src/main.rs
```rust=
use nannou::prelude::*;
use nannou_egui::{self, egui, Egui};
fn main() {
nannou::app(model)
.update(update)
.run();
}
struct Model {
mainspring: f32,
rate: f32,
num_hands: i32,
gear_ratio: i32,
egui: Egui,
}
fn model(app: &App) -> Model {
let main_window = app
.new_window()
.view(view)
.raw_event(raw_window_event)
.build()
.unwrap();
Model {
mainspring: 0.0,
rate: 1.0,
num_hands: 3,
gear_ratio: 10,
egui: Egui::from_window(&app.window(main_window).unwrap()),
}
}
fn update(app: &App, model: &mut Model, _update: Update) {
//turn mainspring
model.mainspring += app.duration.since_prev_update.as_secs_f32() * model.rate;
//egui
let ctx = model.egui.begin_frame();
let app_title = app.exe_name().unwrap();
egui::Window::new(app_title).show(&ctx, |ui| {
ui.label("rate:");
ui.add(egui::Slider::new(&mut model.rate, -10.0..=10.0));
ui.label("number of hands:");
ui.add(egui::Slider::new(&mut model.num_hands, 1..=8));
ui.label("gear ratio:");
ui.add(egui::Slider::new(&mut model.gear_ratio,2..=60));
});
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
draw.background().color(BLACK);
//clock face
let window_bounds = app.window_rect().pad(16.0);
let face_radius = window_bounds.w().min(window_bounds.h())/2.0;
let bounding_box = |pos_func| { map_range(pos_func, -1.0,1.0,-face_radius,face_radius)};
//hands
for h in 0..model.num_hands {
let length = (model.num_hands-h) as f32 / model.num_hands as f32;
let ratio = (model.gear_ratio as f32).pow(h as f32);
let x = bounding_box((model.mainspring/ratio).sin() * length);
let y = bounding_box((model.mainspring/ratio).cos() * length);
let position = Vec2::new(x,y);
draw.ellipse()
.no_fill()
.radius(( face_radius ) * length )
.stroke_color(MIDNIGHTBLUE)
.stroke_weight(2.0);
draw.arrow()
.points(position*-0.25, position)
.stroke_weight(16.0 / (h+1) as f32)
.head_width(20.0 / (h+1) as f32)
.color(FIREBRICK);
}
draw.to_frame(app, &frame).unwrap();
model.egui.draw_to_frame(&frame).unwrap();
}
fn raw_window_event(_app: &App, model: &mut Model, event: &nannou::winit::event::WindowEvent) {
model.egui.handle_raw_event(event);
}
```