Git Product home page Git Product logo

lazywavfiles.jl's Introduction

CI codecov

LazyWAVFiles

This package let's you treat a wav-file on disk as an AbstractArray. Access to the data is lazy, i.e., nothing (but size) is read from the file until the array is indexed into. You can also specify a folder containing many wav-files and treat them all as a single large array! This lets you work using files that are too large to fit in memory. Some examples

using LazyWAVFiles, WAV

# Create some files to work with
d   = mktempdir()
a,b = randn(Float32,10), randn(Float32,10)
WAV.wavwrite(a, joinpath(d,"f1.wav"), Fs=8000)
WAV.wavwrite(b, joinpath(d,"f2.wav"), Fs=8000)

# Indexing into the array loads data from disk
f1 = LazyWAVFile(joinpath(d,"f1.wav")) # This command only reads the size of the file.
f1[1]   == a[1]
f1[1:5] == a[1:5]
f1.fs   == 8000
size(f1)

# We can create an array from all files in a folder
df = DistributedWAVFile(d)       # This reads the size from all files.
df[1]    == a[1]                 # Indexing works the same
df[1:12] == [a; b[1:2]]          # We can even index over both arrays
df[:]    == [a;b]                # Or load all files as one long vector
df.fs    == 8000

size(df) # Other array functions are defined as well
length(df)

# To work using chunks of the entire distributed array, we can use Iterators.partition
julia> Iterators.partition(df, 2) |> collect
10-element Array{Array{Float32,1},1}:
 [0.44920132, -1.1176418]
 [-2.0420709, 0.11797007]
 [1.4723421, -0.32837275]
 [2.3656073, 0.4933495]   
 [-1.0910473, -0.18483315]
 [-0.5574947, -0.46916208]
 [0.27721304, -0.39077175]
 [-0.05172622, -0.715703]
 [0.5821298, 1.6757511]   
 [1.0726295, 0.23483518]

Notes

  • Creating a distributed file based on a folder with a really large number of files can take a while due to the size of each audio clip being read from each file. The size information is required in order to have the files appear as one large array. As an example:
julia> @time df = DistributedWAVFile("folder_with_21551_files/")
 25.518655 seconds (2.47 M allocations: 144.085 MiB, 0.18% gc time)
 DistributedWAVFile{Float32, 1} with 21551 files, 657735677 total datapoints and samplerate 44100.0

lazywavfiles.jl's People

Contributors

baggepinnen avatar github-actions[bot] avatar juliatagbot avatar ymtoo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

ymtoo

lazywavfiles.jl's Issues

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

format="native" vs "double"?

Hi

I was looking for a function that locally reads a chunk of a whole WAV file and found that your module is what I am looking for.

I ran your example cases, and it works well, but I found something weird in my WAV files.

BACKGROUNDS
Interestingly, your lazyWAVFile function returns Int16 from my file, so indexing the array returned Int arrays, so I looked into your codes and found that you use format="native" in the wavread function.
I also went back to WAV.jl to see what the waveread function is like, and the format native will return an encoding type in the file.
I checked my wav file with both parameters

r,fs = wavread(path, format="native", subrange=1)
(Int16[-2], 250000.0f0, 0x0010, WAVChunk[WAVChunk(Symbol("fmt "), UInt8[0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x90, 0xd0, 0x03, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x02, 0x00, 0x10, 0x00]), WAVChunk(:TIME, UInt8[0x66, 0x69, 0x6c, 0x65, 0x14, 0x00, 0x00, 0x00, 0x31, 0x30 … 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x13]), WAVChunk(:bext, UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 … 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])])

r,fs = wavread(path, format="double", subrange=1)
([-6.103701895199438e-5], 250000.0f0, 0x0010, WAVChunk[WAVChunk(Symbol("fmt "), UInt8[0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x90, 0xd0, 0x03, 0x00, 0x20, 0xa1, 0x07, 0x00, 0x02, 0x00, 0x10, 0x00]), WAVChunk(:TIME, UInt8[0x66, 0x69, 0x6c, 0x65, 0x14, 0x00, 0x00, 0x00, 0x31, 0x30 … 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x13]), WAVChunk(:bext, UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 … 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])])

The value (-6.103701895199438e-5) is the exactly same as when I extracted data from the wavread function (of course, since the function uses double as a default)

QUESTIONS

  1. I wonder whether I can go with the format with double why you use the format of native
  2. in that case, how can I modify your module to use? or at least could you update the function to select the format in the argument of the function?
  3. I am not an expert, so I wonder whether the encode type of a file returned from the julia code is absolutely right, or it can be wrong at some points
  4. Should I check my recording setup how it store WAV file with what encode types?

I may ask to WAV.jl as well.

Thanks,

Multichannel files can't be directly indexed

For a 2-channel file x, we get:

> x[1:10]
10×2 Array{Int16,2}:
 7574  -7772
 7111  -7847
 6653  -8045
 6162  -8223
 5626  -8250
 5072  -8171
 4483  -7968
 3869  -7860
 3319  -7825
 2840  -7839

but x[1:10,1] fails:

ArgumentError: step cannot be zero

Stacktrace:
 [1] steprange_last(::Int64, ::Int64, ::Int64) at ./range.jl:212
 [2] StepRange at ./range.jl:202 [inlined]
 [3] _rangestyle at ./range.jl:115 [inlined]
 [4] _range at ./range.jl:113 [inlined]
 [5] #range#39 at ./range.jl:88 [inlined]
 [6] #range at ./none:0 [inlined]
 [7] - at ./range.jl:1014 [inlined]
 [8] (::Colon)(::UnitRange{Int64}, ::UnitRange{Int64}) at ./range.jl:7
 [9] getindex(::LazyWAVFile{Int16,2,Tuple{Int64,Int64}}, ::UnitRange{Int64}, ::Int64) at /Users/mandar/.julia/packages/LazyWAVFiles/XhWcT/src/LazyWAVFiles.jl:30
 [10] top-level scope at In[15]:1

Query sampling rate

It would be nice to return the sampling rate of the file when the file is first opened. With wavread, you'd normally do data, fs = wavread(...). Something similar: data, fs = LazyWAVFile(...) perhaps?

LazyWAVFile hangs without indexing for large files

Replicator:

julia> using LazyWAVFiles, WAV, DSP
julia> WAV.wavwrite(randn(Float32, 1000000), "test.wav", Fs=96000);
julia> x = LazyWAVFile("test.wav");
julia> @time DSP.Periodograms.spectrogram(x[1:end], 512; fs=data.fs);  # works fine
  0.010680 seconds (131 allocations: 15.284 MiB)
julia> @time DSP.Periodograms.spectrogram(x, 512; fs=data.fs)   # never terminates

Performance difference between LazyWAVFile and DistributedWAVFile

By running the following code,

using BenchmarkTools, LazyWAVFiles, WAV

path = mktempdir()
y = sin.((0:99999999)/48000*2pi*440);
wavwrite(y, joinpath(path, "test1.wav"), Fs=48000)

dfile = DistributedWAVFile(path)

indices1 = 1:96000
@btime dfile[indices1]
indices2 = 96000:96000*2
@btime dfile[indices2]
indices3 = 96000*10:96000*11
@btime dfile[indices3]

filepath = joinpath(path, "test1.wav")
lfile = LazyWAVFile(filepath)

indices1 = 1:96000
@btime lfile[indices1]
indices2 = 96000:96000*2
@btime lfile[indices2]
indices3 = 96000*10:96000*11
@btime lfile[indices3]

the output is

2.218 s (5568005 allocations: 306.52 MiB)
2.599 s (5568063 allocations: 306.52 MiB)
2.558 s (5568063 allocations: 306.52 MiB)
181.882 μs (62 allocations: 1.10 MiB)
176.851 μs (62 allocations: 1.10 MiB)
181.919 μs (62 allocations: 1.10 MiB)

The version of Julia in use is

Julia Version 1.4.1
Commit 381693d3df* (2020-04-14 17:20 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-8.0.1 (ORCJIT, skylake)
Environment:
  JULIA_NUM_THREADS = 4

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.