Rust библиотека rodio не запускает mp3, ogg файлы

Хочу запускать музыку через программу на rust. При вводе файла с расширением .wav все прекрасно работает, но вот если это будет mp3, ogg, то выдает такую ошибку:

Введите путь до файла
muse.ogg

thread 'main' panicked at library/core/src/panicking.rs:218:5:
unsafe precondition(s) violated: slice::get_unchecked_mut requires that the index is within the slice

This indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread caused non-unwinding panic. aborting.
[1]    10663 IOT instruction (core dumped)  cargo run src/main.rs

use rodio::{Decoder, OutputStream, Source};
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::thread;
use std::time::Duration;

fn main() {
    create_device();
}

fn create_device() {
    let (_stream, stream_handle) =
        OutputStream::try_default().expect("Не удалось найти поток вывода");

    println!("Введите путь до файла");
    let path = get_file_path();
    let source = listen_file(&path);
    let duration = duration_to_u64(get_audio_duration(&source));

    match stream_handle.play_raw(source.convert_samples()) {
        Ok(()) => {
            println!("Длина композиции: {:?} секунд", duration);
        }

        Err(e) => {
            eprintln!("Ошибка воспроизведения {}", e);
        }
    }

    thread::sleep(Duration::from_secs(duration));
}

fn get_audio_duration(file: &Decoder<BufReader<File>>) -> Option<Duration> {
    file.total_duration()
}

fn duration_to_u64(opt: Option<Duration>) -> u64 {
    match opt {
        Some(dur) => dur.as_secs() as u64,
        None => 0,
    }
}

fn listen_file(path: &str) -> Decoder<BufReader<File>> {
    let file = File::open(path).expect("Файл не найден");
    rodio::Decoder::new(BufReader::new(file)).expect("Ошибка декодирования")
}

fn get_file_path() -> String {
    let mut path = String::new();

    io::stdin()
        .read_line(&mut path)
        .expect("Что ты блять ввел?");

    path.trim().to_string()
}

Не понимаю, что с этим вообще делать


Ответы (1 шт):

Автор решения: Pak Uula

У меня не получилось воспроизвести вашу проблему.

Я собрал приложение с rustc 1.78.0 и rodio v0.20.1

код не работает, но не по той причине, по которой вы пишете.

  1. декодер для OGG возвращает длительность None.
  2. получается что в thread::sleep(Duration::from_secs(duration)); стоит 0 секунд и приложение завершается раньше, чем издаёт хотя бы один звук.

Я заменил длительность на фиксированное значение, и файл заиграл. Я скачал из интернета простой бип-бип, и приложение его успешно вопроизвело

use rodio::{Decoder, OutputStream, Source};
use std::fs::File;
use std::{io};
use std::io::BufReader;
use std::thread;
use std::time::Duration;

fn main() {
    let path = get_path();
    
    create_device(path.as_str());
}

fn get_path() -> String {
    let args: Vec<String> = std::env::args().collect();
    if args.len() > 1 {
        return args[1].clone();
    }

    println!("Введите путь до файла");
    return read_file_path();
}

fn create_device(path: &str) {
    let (_stream, stream_handle) =
        OutputStream::try_default().expect("Не удалось найти поток вывода");

    
    let source = listen_file(&path);
    // let duration = (get_audio_duration(&source));
    let duration = 123;
    match stream_handle.play_raw(source.convert_samples()) {
        Ok(()) => {
            println!("Длина композиции: {:?} секунд", duration);
        }

        Err(e) => {
            eprintln!("Ошибка воспроизведения {}", e);
        }
    }

    thread::sleep(Duration::from_secs(duration));
}

fn get_audio_duration(file: &Decoder<BufReader<File>>) -> Option<Duration> {
    file.total_duration()
}

fn duration_to_u64(opt: Option<Duration>) -> u64 {
    match opt {
        Some(dur) => dur.as_secs() as u64,
        None => 0,
    }
}

fn listen_file(path: &str) -> Decoder<BufReader<File>> {
    let file = BufReader::new(File::open(path).unwrap());
    rodio::Decoder::new(file).expect("Ошибка декодирования")
}

fn read_file_path() -> String {
    let mut path = String::new();

    io::stdin()
        .read_line(&mut path)
        .expect("Что ты блять ввел?");

    path.trim().to_string()
}

Затем я посмотрел, зачем вы используете duration, погуглил немного и обнаружил тип Sink, в котором есть метод sleep_until_end, чтобы дождаться конца воспроизведения.

С Sink всё получилось легко и непринуждённо

use rodio::{Decoder, OutputStream, Sink};
use std::fs::File;
use std::{io};
use std::io::BufReader;

fn main() {
    let path = get_path();
    
    play_file(path.as_str());
}

fn play_file(path: &str) {
    let (_stream, stream_handle) =
        OutputStream::try_default().expect("Не удалось найти поток вывода");

    
    let source = new_decoder(&path);
    let sink = Sink::try_new(&stream_handle).expect("Не удалось создать sink");
    sink.append(source);
    sink.sleep_until_end();
}

fn new_decoder(path: &str) -> Decoder<BufReader<File>> {
    let file = BufReader::new(File::open(path).unwrap());
    rodio::Decoder::new(file).expect("Ошибка декодирования")
}

fn get_path() -> String {
    let args: Vec<String> = std::env::args().collect();
    if args.len() > 1 {
        return args[1].clone();
    }

    println!("Введите путь до файла");
    let mut path = String::new();

    io::stdin()
        .read_line(&mut path)
        .expect("?");

    path.trim().to_string()
}

Работает, не падает.

→ Ссылка