Skip to main content

romy_window/
lib.rs

1use romy_core::input::*;
2use romy_core::runtime::*;
3use romy_core::*;
4use std::cmp::min;
5use std::collections::VecDeque;
6use std::io::Cursor;
7use std::sync::{Arc, RwLock};
8use std::time::{Duration, Instant};
9
10use cpal::traits::{DeviceTrait, EventLoopTrait, HostTrait};
11pub use wgpu;
12use wgpu::Surface;
13use winit::dpi::LogicalSize;
14use winit::event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
15use winit::event_loop::{ControlFlow, EventLoop};
16use winit::window::WindowBuilder;
17
18pub fn run_standalone(app: Box<dyn Game>, info: Info) {
19    run(
20        RunBundle {
21            game: Box::new(GameMutMap::new(app)),
22            info,
23        },
24        |_| None,
25    );
26}
27
28struct RomyGame {
29    bundle: RunBundle,
30    start_time: Instant,
31    step: Duration,
32    steps: u128,
33}
34
35impl RomyGame {
36    fn new(bundle: RunBundle) -> Self {
37        let step = Duration::from_nanos(u64::from(bundle.info.step_interval()));
38
39        Self {
40            bundle,
41            start_time: Instant::now(),
42            step,
43            steps: 0,
44        }
45    }
46}
47
48fn get_spv(bytes: &[u8]) -> Vec<u32> {
49    wgpu::read_spirv(Cursor::new(bytes)).unwrap()
50}
51
52fn convert_virtual_key_code(code: VirtualKeyCode) -> Option<KeyCode> {
53    match code {
54        VirtualKeyCode::Key1 => Some(KeyCode::_1),
55        VirtualKeyCode::Key2 => Some(KeyCode::_2),
56        VirtualKeyCode::Key3 => Some(KeyCode::_3),
57        VirtualKeyCode::Key4 => Some(KeyCode::_4),
58        VirtualKeyCode::Key5 => Some(KeyCode::_5),
59        VirtualKeyCode::Key6 => Some(KeyCode::_6),
60        VirtualKeyCode::Key7 => Some(KeyCode::_7),
61        VirtualKeyCode::Key8 => Some(KeyCode::_8),
62        VirtualKeyCode::Key9 => Some(KeyCode::_9),
63        VirtualKeyCode::Key0 => Some(KeyCode::_0),
64        VirtualKeyCode::A => Some(KeyCode::A),
65        VirtualKeyCode::B => Some(KeyCode::B),
66        VirtualKeyCode::C => Some(KeyCode::C),
67        VirtualKeyCode::D => Some(KeyCode::D),
68        VirtualKeyCode::E => Some(KeyCode::E),
69        VirtualKeyCode::F => Some(KeyCode::F),
70        VirtualKeyCode::G => Some(KeyCode::G),
71        VirtualKeyCode::H => Some(KeyCode::H),
72        VirtualKeyCode::I => Some(KeyCode::I),
73        VirtualKeyCode::J => Some(KeyCode::J),
74        VirtualKeyCode::K => Some(KeyCode::K),
75        VirtualKeyCode::L => Some(KeyCode::L),
76        VirtualKeyCode::M => Some(KeyCode::M),
77        VirtualKeyCode::N => Some(KeyCode::N),
78        VirtualKeyCode::O => Some(KeyCode::O),
79        VirtualKeyCode::P => Some(KeyCode::P),
80        VirtualKeyCode::Q => Some(KeyCode::Q),
81        VirtualKeyCode::R => Some(KeyCode::R),
82        VirtualKeyCode::S => Some(KeyCode::S),
83        VirtualKeyCode::T => Some(KeyCode::T),
84        VirtualKeyCode::U => Some(KeyCode::U),
85        VirtualKeyCode::V => Some(KeyCode::V),
86        VirtualKeyCode::W => Some(KeyCode::W),
87        VirtualKeyCode::X => Some(KeyCode::X),
88        VirtualKeyCode::Y => Some(KeyCode::Y),
89        VirtualKeyCode::Z => Some(KeyCode::Z),
90        VirtualKeyCode::Up => Some(KeyCode::Up),
91        VirtualKeyCode::Down => Some(KeyCode::Down),
92        VirtualKeyCode::Left => Some(KeyCode::Left),
93        VirtualKeyCode::Right => Some(KeyCode::Right),
94        VirtualKeyCode::LBracket => Some(KeyCode::LeftBracket),
95        VirtualKeyCode::RBracket => Some(KeyCode::RightBracket),
96        VirtualKeyCode::Slash => Some(KeyCode::Slash),
97        VirtualKeyCode::Backslash => Some(KeyCode::Backslash),
98        VirtualKeyCode::Comma => Some(KeyCode::Comma),
99        VirtualKeyCode::Period => Some(KeyCode::Period),
100        VirtualKeyCode::Semicolon => Some(KeyCode::Semicolon),
101        _ => None,
102    }
103}
104
105struct Rusty {
106    device: wgpu::Device,
107    queue: wgpu::Queue,
108    surface: wgpu::Surface,
109    vertex_shader_module: wgpu::ShaderModule,
110    fragment_shader_module: wgpu::ShaderModule,
111    texture_extent: wgpu::Extent3d,
112    texture: wgpu::Texture,
113    swap_chain: wgpu::SwapChain,
114    render_pipeline: wgpu::RenderPipeline,
115    bind_group: wgpu::BindGroup,
116    pixels: Vec<u8>,
117    buffer_width: u32,
118    buffer_height: u32,
119    title_prefix: String,
120    sample_rate: Arc<RwLock<u32>>,
121    sound_queue: Arc<RwLock<VecDeque<f32>>>,
122    window: winit::window::Window,
123}
124
125impl Rusty {
126    #[allow(clippy::too_many_arguments)]
127    fn create_pipeline(
128        device: &wgpu::Device,
129        surface: &wgpu::Surface,
130        vertex_shader_module: &wgpu::ShaderModule,
131        fragment_shader_module: &wgpu::ShaderModule,
132        width: u32,
133        height: u32,
134        buffer_width: u32,
135        buffer_height: u32,
136    ) -> (
137        wgpu::Extent3d,
138        wgpu::Texture,
139        wgpu::SwapChain,
140        wgpu::RenderPipeline,
141        wgpu::BindGroup,
142        Vec<u8>,
143    ) {
144        let texture_format = wgpu::TextureFormat::Rgba8UnormSrgb;
145        let texture_extent = wgpu::Extent3d {
146            width: buffer_width,
147            height: buffer_height,
148            depth: 1,
149        };
150
151        let texture = device.create_texture(&wgpu::TextureDescriptor {
152            size: texture_extent,
153            array_layer_count: 1,
154            mip_level_count: 1,
155            sample_count: 1,
156            dimension: wgpu::TextureDimension::D2,
157            format: texture_format,
158            usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
159        });
160
161        let texture_view = texture.create_default_view();
162
163        let swap_chain = device.create_swap_chain(
164            &surface,
165            &wgpu::SwapChainDescriptor {
166                usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
167                format: wgpu::TextureFormat::Bgra8UnormSrgb,
168                width,
169                height,
170                present_mode: wgpu::PresentMode::Vsync,
171            },
172        );
173
174        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
175            address_mode_u: wgpu::AddressMode::ClampToEdge,
176            address_mode_v: wgpu::AddressMode::ClampToEdge,
177            address_mode_w: wgpu::AddressMode::ClampToEdge,
178            mag_filter: wgpu::FilterMode::Nearest,
179            min_filter: wgpu::FilterMode::Nearest,
180            mipmap_filter: wgpu::FilterMode::Nearest,
181            lod_min_clamp: 0.0,
182            lod_max_clamp: 1.0,
183            compare_function: wgpu::CompareFunction::Always,
184        });
185
186        let width_f = width as f32;
187        let height_f = height as f32;
188        let buffer_width_f = buffer_width as f32;
189        let buffer_height_f = buffer_height as f32;
190
191        let scale = (width_f / buffer_width_f).min(height_f / buffer_height_f);
192
193        let scale_width = buffer_width_f * scale / width_f;
194        let scale_height = buffer_height_f * scale / height_f;
195
196        let transform = [
197            scale_width,
198            0.0,
199            0.0,
200            0.0,
201            0.0,
202            scale_height,
203            0.0,
204            0.0,
205            0.0,
206            0.0,
207            1.0,
208            0.0,
209            0.0,
210            0.0,
211            0.0,
212            1.0,
213        ];
214
215        let uniform_buffer = device
216            .create_buffer_mapped(16, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST)
217            .fill_from_slice(&transform);
218
219        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
220            bindings: &[
221                wgpu::BindGroupLayoutBinding {
222                    binding: 0,
223                    visibility: wgpu::ShaderStage::FRAGMENT,
224                    ty: wgpu::BindingType::SampledTexture {
225                        multisampled: false,
226                        dimension: wgpu::TextureViewDimension::D2,
227                    },
228                },
229                wgpu::BindGroupLayoutBinding {
230                    binding: 1,
231                    visibility: wgpu::ShaderStage::FRAGMENT,
232                    ty: wgpu::BindingType::Sampler,
233                },
234                wgpu::BindGroupLayoutBinding {
235                    binding: 2,
236                    visibility: wgpu::ShaderStage::VERTEX,
237                    ty: wgpu::BindingType::UniformBuffer { dynamic: false },
238                },
239            ],
240        });
241
242        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
243            layout: &bind_group_layout,
244            bindings: &[
245                wgpu::Binding {
246                    binding: 0,
247                    resource: wgpu::BindingResource::TextureView(&texture_view),
248                },
249                wgpu::Binding {
250                    binding: 1,
251                    resource: wgpu::BindingResource::Sampler(&sampler),
252                },
253                wgpu::Binding {
254                    binding: 2,
255                    resource: wgpu::BindingResource::Buffer {
256                        buffer: &uniform_buffer,
257                        range: 0..64,
258                    },
259                },
260            ],
261        });
262
263        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
264            bind_group_layouts: &[&bind_group_layout],
265        });
266
267        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
268            layout: &pipeline_layout,
269            vertex_stage: wgpu::ProgrammableStageDescriptor {
270                module: &vertex_shader_module,
271                entry_point: "main",
272            },
273            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
274                module: &fragment_shader_module,
275                entry_point: "main",
276            }),
277            rasterization_state: Some(wgpu::RasterizationStateDescriptor {
278                front_face: wgpu::FrontFace::Ccw,
279                cull_mode: wgpu::CullMode::None,
280                depth_bias: 0,
281                depth_bias_slope_scale: 0.0,
282                depth_bias_clamp: 0.0,
283            }),
284            primitive_topology: wgpu::PrimitiveTopology::TriangleList,
285            color_states: &[wgpu::ColorStateDescriptor {
286                format: wgpu::TextureFormat::Bgra8UnormSrgb,
287                color_blend: wgpu::BlendDescriptor::REPLACE,
288                alpha_blend: wgpu::BlendDescriptor::REPLACE,
289                write_mask: wgpu::ColorWrite::ALL,
290            }],
291            depth_stencil_state: None,
292            index_format: wgpu::IndexFormat::Uint16,
293            vertex_buffers: &[],
294            sample_count: 1,
295            sample_mask: !0,
296            alpha_to_coverage_enabled: false,
297        });
298
299        let capacity = (buffer_width * buffer_height * 4) as usize;
300        let mut pixels: Vec<u8> = Vec::with_capacity(capacity);
301        pixels.resize_with(capacity, Default::default);
302
303        (
304            texture_extent,
305            texture,
306            swap_chain,
307            render_pipeline,
308            bind_group,
309            pixels,
310        )
311    }
312
313    fn new() -> (Self, winit::event_loop::EventLoop<()>) {
314        let title_prefix = String::from("Romy");
315        let buffer_width = 128;
316        let buffer_height = 128;
317        let sample_rate = Arc::new(RwLock::new(44100));
318        let sound_queue = Arc::new(RwLock::new(VecDeque::<f32>::new()));
319
320        let request_adapter_options = wgpu::RequestAdapterOptions::default();
321        let device_descriptor = wgpu::DeviceDescriptor::default();
322        let adapter = wgpu::Adapter::request(&request_adapter_options).unwrap();
323        let (device, queue) = adapter.request_device(&device_descriptor);
324        let vertex_shader_module =
325            device.create_shader_module(&get_spv(include_bytes!("./shaders/vertex.spv")));
326        let fragment_shader_module =
327            device.create_shader_module(&get_spv(include_bytes!("./shaders/fragment.spv")));
328
329        let event_loop = EventLoop::new();
330        let window = {
331            WindowBuilder::new()
332                .with_title(title_prefix.clone())
333                .build(&event_loop)
334                .unwrap()
335        };
336
337        let surface = Surface::create(&window);
338
339        let size = window.inner_size();
340        let (width, height) = size.into();
341
342        let (texture_extent, texture, swap_chain, render_pipeline, bind_group, pixels) =
343            Self::create_pipeline(
344                &device,
345                &surface,
346                &vertex_shader_module,
347                &fragment_shader_module,
348                width,
349                height,
350                buffer_width,
351                buffer_height,
352            );
353
354        (
355            Rusty {
356                device,
357                surface,
358                queue,
359                vertex_shader_module,
360                fragment_shader_module,
361                buffer_width,
362                buffer_height,
363                title_prefix,
364                sample_rate,
365                sound_queue,
366                window,
367                texture_extent,
368                texture,
369                swap_chain,
370                render_pipeline,
371                bind_group,
372                pixels,
373            },
374            event_loop,
375        )
376    }
377
378    fn recreate_pipeline(&mut self) {
379        let size = self.window.inner_size();
380        let (width, height) = size.into();
381
382        let (texture_extent, texture, swap_chain, render_pipeline, bind_group, pixels) =
383            Self::create_pipeline(
384                &self.device,
385                &self.surface,
386                &self.vertex_shader_module,
387                &self.fragment_shader_module,
388                width,
389                height,
390                self.buffer_width,
391                self.buffer_height,
392            );
393
394        self.texture_extent = texture_extent;
395        self.texture = texture;
396        self.swap_chain = swap_chain;
397        self.render_pipeline = render_pipeline;
398        self.bind_group = bind_group;
399        self.pixels = pixels;
400    }
401
402    fn run<F>(
403        mut self,
404        bundle: RunBundle,
405        load_new: F,
406        event_loop: winit::event_loop::EventLoop<()>,
407    ) where
408        F: 'static + Fn(&str) -> Option<RunBundle>,
409    {
410        let mut game = RomyGame::new(bundle);
411
412        let sound_queue_thread = self.sound_queue.clone();
413        let sample_rate_thread = self.sample_rate.clone();
414
415        std::thread::spawn(move || {
416            let sound_queue = sound_queue_thread;
417            let sample_rate = sample_rate_thread;
418
419            let host = cpal::default_host();
420            let device = host
421                .default_output_device()
422                .expect("failed to find a default output device");
423
424            let mut format = device.default_output_format().unwrap();
425            {
426                let mut sample_rate = sample_rate.write().unwrap();
427                *sample_rate = format.sample_rate.0;
428            }
429
430            let audio_event_loop = host.event_loop();
431            let stream_id = audio_event_loop
432                .build_output_stream(&device, &format)
433                .unwrap();
434            audio_event_loop.play_stream(stream_id).unwrap();
435
436            audio_event_loop.run(|id, result| {
437                let data = match result {
438                    Ok(data) => data,
439                    Err(err) => {
440                        eprintln!("an error occurred on stream {:?}: {}", id, err);
441                        return;
442                    }
443                };
444
445                match data {
446                    cpal::StreamData::Output {
447                        buffer: cpal::UnknownTypeOutputBuffer::U16(mut buffer),
448                    } => {
449                        let mut sound_queue = sound_queue.write().unwrap();
450                        for sample in buffer.chunks_mut(format.channels as usize) {
451                            let s = sound_queue.pop_front();
452                            for out in sample.iter_mut() {
453                                *out = match s {
454                                    Some(s) => {
455                                        let s = s / 2.0 + 0.5;
456                                        (s * std::u16::MAX as f32) as u16
457                                    }
458                                    None => 0,
459                                };
460                            }
461                        }
462                    }
463                    cpal::StreamData::Output {
464                        buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer),
465                    } => {
466                        let mut sound_queue = sound_queue.write().unwrap();
467                        for sample in buffer.chunks_mut(format.channels as usize) {
468                            let s = sound_queue.pop_front();
469                            for out in sample.iter_mut() {
470                                *out = match s {
471                                    Some(s) => {
472                                        if s < 0.0 {
473                                            (s * std::i16::MIN as f32) as i16
474                                        } else {
475                                            (s * std::i16::MAX as f32) as i16
476                                        }
477                                    }
478                                    None => 0,
479                                };
480                            }
481                        }
482                    }
483                    cpal::StreamData::Output {
484                        buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer),
485                    } => {
486                        let mut sound_queue = sound_queue.write().unwrap();
487                        for sample in buffer.chunks_mut(format.channels as usize) {
488                            let s = sound_queue.pop_front();
489                            for out in sample.iter_mut() {
490                                *out = match s {
491                                    Some(s) => s,
492                                    None => 0.0,
493                                };
494                            }
495                        }
496                    }
497                    _ => (),
498                }
499            });
500        });
501
502        let mut keyboard = Keyboard::default();
503
504        event_loop.run(move |event, _, control_flow| {
505            *control_flow = ControlFlow::Poll;
506
507            match event {
508                Event::MainEventsCleared => {
509                    self.window.request_redraw();
510                }
511                Event::DeviceEvent {
512                    event:
513                        DeviceEvent::Key(
514                            KeyboardInput {
515                                state: ElementState::Pressed,
516                                virtual_keycode: Some(code),
517                                ..
518                            },
519                            ..,
520                        ),
521                    ..
522                } => {
523                    let code = convert_virtual_key_code(code);
524                    if let Some(code) = code {
525                        keyboard.key_down(code);
526                    }
527                }
528                Event::DeviceEvent {
529                    event:
530                        DeviceEvent::Key(
531                            KeyboardInput {
532                                state: ElementState::Released,
533                                virtual_keycode: Some(code),
534                                ..
535                            },
536                            ..,
537                        ),
538                    ..
539                } => {
540                    let code = convert_virtual_key_code(code);
541                    if let Some(code) = code {
542                        keyboard.key_up(code);
543                    }
544                }
545                Event::WindowEvent {
546                    event:
547                        WindowEvent::KeyboardInput {
548                            input:
549                                KeyboardInput {
550                                    state: ElementState::Pressed,
551                                    virtual_keycode: Some(code),
552                                    ..
553                                },
554                            ..
555                        },
556                    ..
557                } => {
558                    let code = convert_virtual_key_code(code);
559                    if let Some(code) = code {
560                        keyboard.key_down(code);
561                    }
562                }
563                Event::WindowEvent {
564                    event:
565                        WindowEvent::KeyboardInput {
566                            input:
567                                KeyboardInput {
568                                    state: ElementState::Released,
569                                    virtual_keycode: Some(code),
570                                    ..
571                                },
572                            ..
573                        },
574                    ..
575                } => {
576                    let code = convert_virtual_key_code(code);
577                    if let Some(code) = code {
578                        keyboard.key_up(code);
579                    }
580                }
581                Event::RedrawRequested(_) => {
582                    let size = self.window.inner_size();
583
584                    let (width, height): (u32, u32) = size.into();
585
586                    let app = &mut game.bundle.game;
587
588                    let time_span = Instant::now().duration_since(game.start_time);
589                    let step_offset = (time_span.as_micros() % game.step.as_micros()) as f32
590                        / game.step.as_micros() as f32;
591
592                    let render = app.draw(&DrawArguments::new(
593                        width as i32,
594                        height as i32,
595                        step_offset,
596                    ));
597                    if self.buffer_width != render.width() as u32
598                        || self.buffer_height != render.height() as u32
599                    {
600                        self.buffer_width = render.width() as u32;
601                        self.buffer_height = render.height() as u32;
602                        self.recreate_pipeline();
603                    }
604
605                    let source = render.pixels8();
606                    self.pixels.clone_from_slice(&source[..]);
607
608                    let swap_chain_texture = self.swap_chain.get_next_texture();
609
610                    let mut encoder = self
611                        .device
612                        .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
613
614                    let buffer = self
615                        .device
616                        .create_buffer_mapped(self.pixels.len(), wgpu::BufferUsage::COPY_SRC)
617                        .fill_from_slice(&self.pixels);
618
619                    encoder.copy_buffer_to_texture(
620                        wgpu::BufferCopyView {
621                            buffer: &buffer,
622                            offset: 0,
623                            row_pitch: self.texture_extent.width * 4,
624                            image_height: self.texture_extent.height,
625                        },
626                        wgpu::TextureCopyView {
627                            texture: &self.texture,
628                            mip_level: 0,
629                            array_layer: 0,
630                            origin: wgpu::Origin3d {
631                                x: 0.0,
632                                y: 0.0,
633                                z: 0.0,
634                            },
635                        },
636                        self.texture_extent,
637                    );
638
639                    {
640                        let mut render_pass =
641                            encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
642                                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
643                                    attachment: &swap_chain_texture.view,
644                                    resolve_target: None,
645                                    load_op: wgpu::LoadOp::Clear,
646                                    store_op: wgpu::StoreOp::Store,
647                                    clear_color: wgpu::Color::BLACK,
648                                }],
649                                depth_stencil_attachment: None,
650                            });
651                        render_pass.set_pipeline(&self.render_pipeline);
652                        render_pass.set_bind_group(0, &self.bind_group, &[]);
653                        render_pass.draw(0..6, 0..1);
654                    }
655
656                    self.queue.submit(&[encoder.finish()]);
657                }
658                Event::WindowEvent {
659                    event: WindowEvent::DroppedFile(file_path),
660                    ..
661                } => {
662                    if let Some(bundle) = load_new(file_path.to_str().unwrap()) {
663                        self.window.set_title(&format!(
664                            "{}: {}",
665                            self.title_prefix,
666                            bundle.info.name()
667                        ));
668                        game = RomyGame::new(bundle);
669                    }
670                }
671                Event::WindowEvent {
672                    event: WindowEvent::Resized(_),
673                    ..
674                } => {
675                    self.recreate_pipeline();
676                }
677                Event::WindowEvent {
678                    event: WindowEvent::CloseRequested,
679                    ..
680                } => *control_flow = ControlFlow::Exit,
681                _ => (),
682            }
683
684            let mut input = InputCollection::new();
685            input.add_input(InputDevice::Keyboard(keyboard.clone()));
686
687            let time_span = Instant::now().duration_since(game.start_time);
688            let expected_steps = time_span.as_micros() / game.step.as_micros();
689            let app = &mut game.bundle.game;
690            let info = &game.bundle.info;
691
692            if game.steps < expected_steps {
693                app.step(&StepArguments::new(input.get_input_arguments(&info)));
694
695                let audio = app.render_audio(&RenderAudioArguments {});
696
697                if audio.sample_rate() as u32 % game.bundle.info.steps_per_second() != 0 {
698                    panic!("Sample rate not divisible by steps per second");
699                }
700
701                let expected_sample_count =
702                    audio.sample_rate() as u32 / game.bundle.info.steps_per_second();
703                if expected_sample_count != audio.samples().len() as u32 {
704                    panic!("Incorrect number of samples supplied");
705                }
706                let sample_rate = { *self.sample_rate.read().unwrap() };
707                let sample_count = sample_rate / game.bundle.info.steps_per_second();
708
709                let mut sound_queue = self.sound_queue.write().unwrap();
710                let ratio = expected_sample_count as f32 / sample_count as f32;
711
712                for t in 0..sample_count {
713                    let t = t as f32 * ratio;
714                    let low = audio.samples()[t.floor() as usize];
715                    let high = audio.samples()
716                        [min(t.ceil() as usize, (expected_sample_count - 1) as usize)];
717                    sound_queue.push_back((low + high) / 2.0);
718                }
719
720                game.steps += 1;
721            }
722        });
723    }
724}
725
726/// Runs a RunBundle
727///
728/// # Arguments
729/// * `bundle` - Optional bundle to run, if none is supplied the sdl window will open and wait
730/// for a game do be dropped onto it.
731/// * `load_new` - Callback to get a new bundle from a file path, this will be called if a file is
732/// dragged onto the game window.
733pub fn run<F>(bundle: RunBundle, load_new: F)
734where
735    F: 'static + Fn(&str) -> Option<RunBundle>,
736{
737    let (rusty, event_loop) = Rusty::new();
738    rusty.run(bundle, load_new, event_loop);
739}