Giter Club home page Giter Club logo

bwavfile's Introduction

Crates.io Crates.io GitHub last commit GitHub Workflow Status

bwavfile

Wave File Reader/Writer library in Rust, with Broadcast-WAV, RF64 and production metadata support

Features

bwavfile provides a reader WaveReader and writer type WaveWriter for reading and creating new wave audio files.

WaveReader and WaveWriter support:

  • A unified interface for standard RIFF and RF64/BW64 64-bit Wave files.
  • When using WaveWriter, wave files are transparently upgraded from RIFF to RF64 when required.
  • Unpacked reading and writing of Integer PCM and IEEE float audio data formats.
  • A unified interface for standard WaveFormat and extended WaveFormatEx wave data format.

The library has extensive metadata support, with emphasis on film and video production metadata:

  • Broadcast-Wave metadata extension, including long description, originator, SMPTE UMID and coding history.
  • Reading and writing of embedded iXML and axml/ADM metadata.
  • Reading and writing of timed cues and and timed cue regions.
  • Multichannel, surround, and ambisonic audio data description including surround channel maps, ADM AudioTrackFormat, AudioChannelFormatRef and AudioPackRef data structures.

Feature Roadmap

Some features that may be included in the future include:

  • Broadcast-Wave levl waveform overview data reading and writing.
  • Sampler and Instrument metadata.
  • Performance improvements.

Use Examples

  • blits shows how to use WaveWriter to create a new file with BLITS alignment tones.
  • wave-inter uses WaveReader and WaveWriter to interleave several input Wave files into a single polyphonic Wave file.
  • wave-deinter uses WaveReader and WaveWriter to de-interleave an input Wave file into several monoarual Wave files.

Note on Testing

All of the media for the integration tests is committed to the respository in zipped form. Before you can run tests, you need to cd into the tests directory and run the create_test_media.sh script. Note that one of the test files (the RF64 test case) is over four gigs in size.

bwavfile's People

Contributors

atoav avatar iluvcapra avatar irh avatar the-eater avatar wuelle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

bwavfile's Issues

ADM Metadata

Implement the ChannelDescriptors structure and ADM metadata access.

Cue Points from Sound Devices recorder are read, but return wrong frame

I am currently building something based on this project (and things are going quite well, thanks to good work). I noticed however that the cue_points() method doesn't seem to work as expected with the queue points that a SoundDevices MixPre-series recorder produces when the cue point button is pressed.

Expectation

Running cue_points() should return a Result<Vec<Cue>, ParserError> where Cue::frame reflects the Cue's position.

Problem

In the case of the Sound Devices file it returns a list of Cues in the correct length, however the value of the frame field is always zero.

Details

When I open the File in Izotope RX and save it works as expected. That prompts the question: What is actually different between the files?

Sound Devices

See the file here: sounddevices.zip

hex1

Chunk in Hex:

63 75 65 20 94 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 64 61 74 61 00 00 00 00 00 00 00 00 00 60 01 00 01 00 00 00 00 00 00 00 64 61 74 61 00 00 00 00 00 00 00 00 00 B0 02 00 02 00 00 00 00 00 00 00 64 61 74 61 00 00 00 00 00 00 00 00 00 A0 03 00 03 00 00 00 00 00 00 00 64 61 74 61 00 00 00 00 00 00 00 00 00 80 04 00 04 00 00 00 00 00 00 00 64 61 74 61 00 00 00 00 00 00 00 00 00 D0 05 00 05 00 00 00 00 00 00 00 64 61 74 61 00 00 00 00 00 00 00 00 00 E0 05 00

Izotope

Versus the file here: izotope.zip

hex2

Chunk in Hex:

63 75 65 20 94 00 00 00 06 00 00 00 01 00 00 00 00 60 01 00 64 61 74 61 00 00 00 00 00 00 00 00 00 60 01 00 02 00 00 00 00 B0 02 00 64 61 74 61 00 00 00 00 00 00 00 00 00 B0 02 00 03 00 00 00 00 A0 03 00 64 61 74 61 00 00 00 00 00 00 00 00 00 A0 03 00 04 00 00 00 00 80 04 00 64 61 74 61 00 00 00 00 00 00 00 00 00 80 04 00 05 00 00 00 00 D0 05 00 64 61 74 61 00 00 00 00 00 00 00 00 00 D0 05 00 06 00 00 00 00 E0 05 00 64 61 74 61 00 00 00 00 00 00 00 00 00 E0 05 00 4C 49 53 54 04 00 00 00 61 64 74 6C

Differences

The most obvious difference here is that the sounddevices file has no adtl chunk. I deleted that part in the izotope.wav with a hexeditor and the result still came out right, so I assume this plays no role.

I spotted two differences which might have more impact here (pictured here, the first cue point):

grafik

  1. Sound Devices starts counting with an ID of 0, iZotope with an ID of 1
  2. Sound Devices leaves the value for Position at 0, iZotope fills it with the value both use in the Sample Offset field

The technical documentation linked in your Docs defines Position as:

The position specifies the sample offset associated with the cue point in terms of the sample's position in the final stream of samples generated by the play list. Said in another way, if a play list chunk is specified, the position value is equal to the sample number at which this cue point will occur during playback of the entire play list as defined by the play list's order. If no play list chunk is specified this value should be 0.

I can't find any plst chunk in the data, so iZotopes implementation is probably at fault here (or I just don't know enough about the matter). iZotope RX itself can read the Sound Devices cue chunks however, otherwise it would not be able to save them in their format. Because reliably reading cue points when they are there is probably the better option over not reading them because they don't fit the precisely defined format.

Cross checking

ffprobe has an option to list cues as well. When I run ffprobe -i sounddevices.wav -print_format json -show_chapters -loglevel error I get the following (expected) data:

{
    "chapters": [
        {
            "id": 0,
            "time_base": "1/44100",
            "start": 90112,
            "start_time": "2.043356",
            "end": 176128,
            "end_time": "3.993832"
        },
        {
            "id": 1,
            "time_base": "1/44100",
            "start": 176128,
            "start_time": "3.993832",
            "end": 237568,
            "end_time": "5.387029"
        },
        {
            "id": 2,
            "time_base": "1/44100",
            "start": 237568,
            "start_time": "5.387029",
            "end": 294912,
            "end_time": "6.687347"
        },
        {
            "id": 3,
            "time_base": "1/44100",
            "start": 294912,
            "start_time": "6.687347",
            "end": 380928,
            "end_time": "8.637823"
        },
        {
            "id": 4,
            "time_base": "1/44100",
            "start": 380928,
            "start_time": "8.637823",
            "end": 385024,
            "end_time": "8.730703"
        },
        {
            "id": 5,
            "time_base": "1/44100",
            "start": 385024,
            "start_time": "8.730703",
            "end": 452932,
            "end_time": "10.270567"
        }
    ]
}

Fix

Maybe changing this in cue.rs this would already fix it?
It is likely that I am too optimistic here though

            raw_cues.iter()
            .map(|i| {
                Cue {
                    //ident : i.cue_point_id,
                    frame : i.frame_offset, # <-- was i.frame,
                    length: {
                        raw_adtl.ltxt_for_cue_point(i.cue_point_id).first()
                        .filter(|x| x.purpose == FourCC::make(b"rgn "))
                        .map(|x| x.frame_length)
                    },
                    label: {
                        raw_adtl.labels_for_cue_point(i.cue_point_id).iter()
                            .map(|s| convert_to_cue_string(&s.text))
                            .next()
                    },
                    note : {
                        raw_adtl.notes_for_cue_point(i.cue_point_id).iter()
                            //.filter_map(|x| str::from_utf8(&x.text).ok())
                            .map(|s| convert_to_cue_string(&s.text))
                            .next()
                    }
                }
            }).collect() 

Channel deinter/splitting is slow?

I tried the wave-deinter example and while it worked, I found it to be quite slow compared to existing solutions (I tried with a 10-Channel 32-Bit wav with 540 MB of file size)

I managed to speed things up a little by moving from this:

  1. iterating over channels
  2. reading the input frames for each
  3. writing the appropriate channel to the output

to that:

  1. creating output files and readers for each output channel
  2. reading the input frames once
  3. writing the appropriate sample to each output writer

Additionally I could speed up by using rayon::par_iter_mut to write the outputs.

The result is still magnitudes slower than splitting the same wav file using sox or ffmpeg. I am not to familiar with the underlying implementation of bwavfile, but I suspect there is potential for optimization?

bwavfile::raw_chunk_reader is a private module

I'm very new to Rust so I may be doing something wrong - I'm trying to write a function that returns an AudioFrameReader, this is the function:

fn from_wav_filename(wav_filename: &str) -> Result<(WaveFmt, AudioFrameReader<RawChunkReader<std::fs::File>>), ()> {
    if let mut Ok(r) = WaveReader::open(&wav_filename) {
        let format = r.format().unwrap();
        let mut frame_reader = r.audio_frame_reader().unwrap();
        Ok((format, frame_reader))
    } else {
        Err(())
    }
}

However, compilation fails because bwavfile::raw_chunk_reader is a private module:

error[E0603]: module `raw_chunk_reader` is private
   --> src/main.rs:2:15
    |
2   | use bwavfile::raw_chunk_reader::RawChunkReader;
    |               ^^^^^^^^^^^^^^^^ private module
    |

It seems like I'd run into the same issue if I tried adding AudioFrameReader to a custom struct type.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.