diff --git a/REQUIRE b/REQUIRE index cdd3023..294bc41 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,2 +1,3 @@ julia 0.6 FileIO 0.2.0 +Compat 0.62 diff --git a/src/AudioDisplay.jl b/src/AudioDisplay.jl index 6e10b89..19c449d 100644 --- a/src/AudioDisplay.jl +++ b/src/AudioDisplay.jl @@ -1,4 +1,5 @@ import Base.show +using Compat.Base64: base64encode struct WAVArray{T,N} Fs::Number diff --git a/src/WAV.jl b/src/WAV.jl index 5562582..a5fd342 100644 --- a/src/WAV.jl +++ b/src/WAV.jl @@ -6,11 +6,13 @@ export WAVChunk, WAVMarker, wav_cue_read, wav_cue_write, wav_info_write, wav_inf export WAVArray, WAVFormatExtension, WAVFormat export isextensible, isformat, bits_per_sample export WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_ALAW, WAVE_FORMAT_MULAW +using Compat: codeunits, findall, nothing, Nothing, undef +import Compat: Libdl using FileIO function __init__() module_dir = dirname(@__FILE__) - if Libdl.find_library(["libpulse-simple"]) != "" + if Libdl.find_library(["libpulse-simple", "libpulse-simple.so.0"]) != "" include(joinpath(module_dir, "wavplay-pulse.jl")) elseif Libdl.find_library(["AudioToolbox"], ["/System/Library/Frameworks/AudioToolbox.framework/Versions/A"]) != "" @@ -33,7 +35,7 @@ struct WAVFormatExtension nbits::UInt16 # overrides nbits in WAVFormat type channel_mask::UInt32 sub_format::Array{UInt8, 1} # 16 byte GUID - WAVFormatExtension() = new(0, 0, Array{UInt8, 1}(0)) + WAVFormatExtension() = new(0, 0, UInt8[]) WAVFormatExtension(nb, cm, sb) = new(nb, cm, sb) end @@ -100,7 +102,7 @@ end function isformat(fmt::WAVFormat, code) if code != WAVE_FORMAT_EXTENSIBLE && isextensible(fmt) - subtype = Array{UInt8, 1}(0) + subtype = UInt8[] if code == WAVE_FORMAT_PCM subtype = KSDATAFORMAT_SUBTYPE_PCM elseif code == WAVE_FORMAT_IEEE_FLOAT @@ -130,7 +132,7 @@ end function read_header(io::IO) # check if the given file has a valid RIFF header - riff = Array{UInt8}(4) + riff = Vector{UInt8}(undef, 4) read!(io, riff) if riff != b"RIFF" error("Invalid WAV file: The RIFF header is invalid") @@ -139,7 +141,7 @@ function read_header(io::IO) chunk_size = read_le(io, UInt32) # check if this is a WAV file - format = Array{UInt8}(4) + format = Vector{UInt8}(undef, 4) read!(io, format) if format != b"WAVE" error("Invalid WAV file: the format is not WAVE") @@ -167,12 +169,12 @@ function read_format(io::IO, chunk_size::UInt32) bytes_per_second = read_le(io, UInt32) block_align = read_le(io, UInt16) nbits = read_le(io, UInt16) - ext = Array{UInt8, 1}(0) + ext = UInt8[] chunk_size -= 16 if chunk_size > 0 extra_bytes_length = read_le(io, UInt16) if extra_bytes_length == 22 - ext = Array{UInt8}(extra_bytes_length) + ext = Vector{UInt8}(undef, extra_bytes_length) read!(io, ext) end end @@ -238,9 +240,9 @@ ieee_float_container_type(nbits) = (nbits == 32 ? Float32 : (nbits == 64 ? Float function read_pcm_samples(io::IO, fmt::WAVFormat, subrange) nbits = bits_per_sample(fmt) if isempty(subrange) - return Array{pcm_container_type(nbits), 2}(0, fmt.nchannels) + return Array{pcm_container_type(nbits), 2}(undef, 0, fmt.nchannels) end - samples = Array{pcm_container_type(nbits), 2}(length(subrange), fmt.nchannels) + samples = Array{pcm_container_type(nbits), 2}(undef, length(subrange), fmt.nchannels) sample_type = eltype(samples) nbytes = ceil(Integer, nbits / 8) bitshift = [0x0, 0x8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40] @@ -251,7 +253,7 @@ function read_pcm_samples(io::IO, fmt::WAVFormat, subrange) skip(io, convert(UInt, (first(subrange) - 1) * nbytes * fmt.nchannels)) for i = 1:size(samples, 1) for j = 1:size(samples, 2) - raw_sample = Array{UInt8}(nbytes) + raw_sample = Vector{UInt8}(undef, nbytes) read!(io, raw_sample) my_sample = UInt64(0) for k = 1:nbytes @@ -268,10 +270,10 @@ end function read_ieee_float_samples(io::IO, fmt::WAVFormat, subrange, floatType) if isempty(subrange) - return Array{floatType, 2}(0, fmt.nchannels) + return Array{floatType, 2}(undef, 0, fmt.nchannels) end nblocks = length(subrange) - samples = Array{floatType, 2}(nblocks, fmt.nchannels) + samples = Array{floatType, 2}(undef, nblocks, fmt.nchannels) nbits = bits_per_sample(fmt) skip(io, convert(UInt, (first(subrange) - 1) * (nbits / 8) * fmt.nchannels)) for i = 1:nblocks @@ -290,10 +292,10 @@ end function read_companded_samples(io::IO, fmt::WAVFormat, subrange, table) if isempty(subrange) - return Array{eltype(table), 2}(0, fmt.nchannels) + return Array{eltype(table), 2}(undef, 0, fmt.nchannels) end nblocks = length(subrange) - samples = Array{eltype(table), 2}(nblocks, fmt.nchannels) + samples = Array{eltype(table), 2}(undef, nblocks, fmt.nchannels) skip(io, convert(UInt, (first(subrange) - 1) * fmt.nchannels)) for i = 1:nblocks for j = 1:fmt.nchannels @@ -517,7 +519,11 @@ function read_data(io::IO, chunk_size, fmt::WAVFormat, format, subrange) # "format" is the format of values, while "fmt" is the WAV file level format convert_to_double = x -> convert(Array{Float64}, x) - if subrange === Void + if subrange === Nothing + Base.depwarn("`wavread(..., subrange=Nothing)` is deprecated, use `wavread(..., subrange=:)` instead.", :read_data) + subrange = (:) + end + if subrange === (:) # each block stores fmt.nchannels channels subrange = 1:convert(UInt, chunk_size / fmt.block_align) end @@ -603,7 +609,7 @@ end make_range(subrange) = subrange make_range(subrange::Number) = 1:convert(Int, subrange) -function wavread(io::IO; subrange=Void, format="double") +function wavread(io::IO; subrange=(:), format="double") chunk_size = read_header(io) samples = Array{Float64, 1}() nbits = 0 @@ -621,7 +627,7 @@ function wavread(io::IO; subrange=Void, format="double") fmt = WAVFormat() while chunk_size >= subchunk_header_size # Read subchunk ID and size - subchunk_id = Array{UInt8}(4) + subchunk_id = Vector{UInt8}(undef, 4) read!(io, subchunk_id) subchunk_size = read_le(io, UInt32) if subchunk_size > chunk_size @@ -641,7 +647,7 @@ function wavread(io::IO; subrange=Void, format="double") end samples = read_data(io, subchunk_size, fmt, format, make_range(subrange)) else - subchunk_data = Array{UInt8}(subchunk_size) + subchunk_data = Vector{UInt8}(undef, subchunk_size) read!(io, subchunk_data) push!(opt, WAVChunk(Symbol(subchunk_id), subchunk_data)) end @@ -649,7 +655,7 @@ function wavread(io::IO; subrange=Void, format="double") return samples, sample_rate, nbits, opt end -function wavread(filename::AbstractString; subrange=Void, format="double") +function wavread(filename::AbstractString; subrange=(:), format="double") open(filename, "r") do io wavread(io, subrange=subrange, format=format) end @@ -699,7 +705,7 @@ function wavwrite(samples::AbstractArray, io::IO; Fs=8000, nbits=0, compression= compression_code = WAVE_FORMAT_EXTENSIBLE valid_bits_per_sample = nbits channel_mask = 0 - sub_format = Array{UInt8, 1}(0) + sub_format = UInt8[] if compression == WAVE_FORMAT_PCM sub_format = KSDATAFORMAT_SUBTYPE_PCM elseif compression == WAVE_FORMAT_IEEE_FLOAT @@ -749,7 +755,7 @@ end function wavappend(samples::AbstractArray, io::IO) seekstart(io) chunk_size = read_header(io) - subchunk_id = Array{UInt8}(4) + subchunk_id = Vector{UInt8}(undef, 4) read!(io, subchunk_id) subchunk_size = read_le(io, UInt32) if subchunk_id != b"fmt " @@ -761,18 +767,18 @@ function wavappend(samples::AbstractArray, io::IO) error("Number of channels do not match") end - # Compute data length of current chunk to-be-appended. + # Compute data length of current chunk to-be-appended. data_length = size(samples, 1) * fmt.block_align - # Update `chunksize`: add length of new data. + # Update `chunksize`: add length of new data. seek(io,4) write_le(io, convert(UInt32, chunk_size + data_length)) - # Get `subchunk2size`. - seek(io,40) + # Get `subchunk2size`. + seek(io, 24 + subchunk_size) data_length_old = read_le(io, UInt32) - # Update `subchunk2size`: add length of new data. - seek(io,40) + # Update `subchunk2size`: add length of new data. + seek(io, 24 + subchunk_size) write_le(io, convert(UInt32, data_length_old + data_length)) - + seekend(io) write_data(io, fmt, samples) end diff --git a/src/WAVChunk.jl b/src/WAVChunk.jl index 6120980..14351d5 100644 --- a/src/WAVChunk.jl +++ b/src/WAVChunk.jl @@ -1,7 +1,7 @@ """ A RIFF chunk. """ -type WAVChunk +struct WAVChunk id::Symbol data::Vector{UInt8} end @@ -9,7 +9,7 @@ end """ A marker in a .wav file. `start_time` and `duration` are in samples. """ -type WAVMarker +mutable struct WAVMarker label::String start_time::UInt32 duration::UInt32 @@ -98,8 +98,8 @@ function wav_cue_read(chunks::Vector{WAVChunk}) markers = Dict{UInt32, WAVMarker}() # See if list and cue chunks are present - list_chunks = chunks[find(c -> c.id == :LIST, chunks)] - cue_chunks = chunks[find(c -> c.id == Symbol("cue "), chunks)] + list_chunks = chunks[findall(c -> c.id == :LIST, chunks)] + cue_chunks = chunks[findall(c -> c.id == Symbol("cue "), chunks)] for l in list_chunks read_list(markers, l.data) @@ -132,12 +132,12 @@ function write_marker_list(markers::Dict{UInt32, WAVMarker}) # Create all the labl entries for (cue_id, marker) in markers - labl = [write32(cue_id); Vector{UInt8}(marker.label); 0x0] + labl = [write32(cue_id); codeunits(marker.label); 0x0] # The note and label entries must have an even number of bytes. # So, for the null terminated text in the label and note, we add a minimum of # one null terminator, but if that creates an odd number of bytes in the labl - # or note entry, then add a second null terminator. + # or note entry, then add a second null terminator. if (length(labl) % 2) == 1 labl = [labl; 0x0] end @@ -171,17 +171,17 @@ function wav_info_write(tags::Dict{Symbol, String}) # Create all the tag entries for t in keys(tags) - tag = [Vector{UInt8}(tags[t]); 0x0] + tag = [codeunits(tags[t]); 0x0] # The tag entries must have an even number of bytes. # So, for the null terminated text in the tag, we add a minimum of # one null terminator, but if that creates an odd number of bytes in the tag - # or note entry, then add a second null terminator. + # or note entry, then add a second null terminator. if (length(tag) % 2) == 1 tag = [tag; 0x0] end - info_data = [info_data; Vector{UInt8}(String(t)); write32(UInt32(length(tag))); tag] + info_data = [info_data; codeunits(String(t)); write32(UInt32(length(tag))); tag] end [WAVChunk(:LIST, info_data)] end @@ -215,7 +215,7 @@ https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/RIFF.html#Info function wav_info_read(chunks::Vector{WAVChunk}) tags = Dict{Symbol, String}() - list_chunks = chunks[find(c -> c.id == :LIST, chunks)] + list_chunks = chunks[findall(c -> c.id == :LIST, chunks)] for l in list_chunks list_data = l.data if list_data[1:4] == b"INFO" diff --git a/src/wavplay-audioqueue.jl b/src/wavplay-audioqueue.jl index 4053938..6e2b3b7 100644 --- a/src/wavplay-audioqueue.jl +++ b/src/wavplay-audioqueue.jl @@ -1,12 +1,14 @@ # -*- mode: julia; -*- module WAVPlay -import WAV.wavplay +import ..wavplay + +using Compat: Cvoid, undef const OSStatus = Int32 -const CFTypeRef = Ptr{Void} -const CFRunLoopRef = Ptr{Void} -const CFStringRef = Ptr{Void} -const AudioQueueRef = Ptr{Void} +const CFTypeRef = Ptr{Cvoid} +const CFRunLoopRef = Ptr{Cvoid} +const CFStringRef = Ptr{Cvoid} +const AudioQueueRef = Ptr{Cvoid} # format IDs const kAudioFormatLinearPCM = # "lpcm" @@ -61,9 +63,9 @@ end # Apple Core Audio Type mutable struct AudioQueueBuffer mAudioDataBytesCapacity::UInt32 - mAudioData::Ptr{Void} + mAudioData::Ptr{Cvoid} mAudioDataByteSize::UInt32 - mUserData::Ptr{Void} + mUserData::Ptr{Cvoid} mPacketDescriptionCapacity::UInt32 mPacketDescription::Ptr{AudioStreamPacketDescription} mPacketDescriptionCount::UInt32 @@ -78,8 +80,8 @@ const AudioToolbox = "/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox" CFRunLoopGetCurrent() = ccall((:CFRunLoopGetCurrent, CoreFoundation), CFRunLoopRef, ()) -CFRunLoopRun() = ccall((:CFRunLoopRun, CoreFoundation), Void, ()) -CFRunLoopStop(rl) = ccall((:CFRunLoopStop, CoreFoundation), Void, (CFRunLoopRef, ), rl) +CFRunLoopRun() = ccall((:CFRunLoopRun, CoreFoundation), Cvoid, ()) +CFRunLoopStop(rl) = ccall((:CFRunLoopStop, CoreFoundation), Cvoid, (CFRunLoopRef, ), rl) getCoreFoundationRunLoopDefaultMode() = unsafe_load(cglobal((:kCFRunLoopDefaultMode, CoreFoundation), CFStringRef)) @@ -146,7 +148,7 @@ end # audio queue buffer structure, AudioQueueBuffer, is initially set to 0. # @result An OSStatus result code. function AudioQueueAllocateBuffer(aq) - newBuffer = Array{AudioQueueBufferRef, 1}(1) + newBuffer = Array{AudioQueueBufferRef, 1}(undef, 1) result = ccall((:AudioQueueAllocateBuffer, AudioToolbox), OSStatus, (AudioQueueRef, UInt32, Ptr{AudioQueueBufferRef}), @@ -175,7 +177,7 @@ function AudioQueueEnqueueBuffer(aq, bufPtr, data) unsafe_store!(bufPtr, buffer) result = ccall((:AudioQueueEnqueueBuffer, AudioToolbox), OSStatus, - (AudioQueueRef, AudioQueueBufferRef, UInt32, Ptr{Void}), + (AudioQueueRef, AudioQueueBufferRef, UInt32, Ptr{Cvoid}), aq, bufPtr, 0, C_NULL) if result != 0 error("AudioQueueEnqueueBuffer failed with $result") @@ -263,12 +265,12 @@ function AudioQueueNewOutput(format::AudioStreamBasicDescription, userData::Audi userData.runLoop = runLoop runLoopMode = getCoreFoundationRunLoopDefaultMode() - newAudioQueue = Array{AudioQueueRef, 1}(1) - cCallbackProc = cfunction(playCallback, Void, + newAudioQueue = Array{AudioQueueRef, 1}(undef, 1) + cCallbackProc = cfunction(playCallback, Cvoid, (Ptr{AudioQueueData}, AudioQueueRef, AudioQueueBufferRef)) result = ccall((:AudioQueueNewOutput, AudioToolbox), OSStatus, - (Ptr{AudioStreamBasicDescription}, Ptr{Void}, Ptr{AudioQueueData}, CFRunLoopRef, CFStringRef, UInt32, Ptr{AudioQueueRef}), + (Ptr{AudioStreamBasicDescription}, Ptr{Cvoid}, Ptr{AudioQueueData}, CFRunLoopRef, CFStringRef, UInt32, Ptr{AudioQueueRef}), Ref(format), cCallbackProc, Ref(userData), runLoop, runLoopMode, 0, newAudioQueue) if result != 0 error("AudioQueueNewOutput failed with $result") diff --git a/src/wavplay-pulse.jl b/src/wavplay-pulse.jl index 7acba14..a83dfc0 100644 --- a/src/wavplay-pulse.jl +++ b/src/wavplay-pulse.jl @@ -1,6 +1,9 @@ # -*- mode: julia; -*- module WAVPlay -import WAV.wavplay +import ..wavplay + +using Compat: Cvoid, undef +import Compat: Libdl # typedef enum pa_sample_format const PA_SAMPLE_U8 = 0 # Unsigned 8 Bit PCM @@ -71,8 +74,8 @@ struct pa_buffer_attr fragsize::UInt32 end -const pa_simple = Ptr{Void} -const LibPulseSimple = "libpulse-simple" +const pa_simple = Ptr{Cvoid} +const LibPulseSimple = Libdl.find_library(["libpulse-simple", "libpulse-simple.so.0"]) const PA_STREAM_PLAYBACK = 1 const PA_CHANNEL_MAP_AIFF = 0 const PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF @@ -83,7 +86,7 @@ function wavplay(data, fs) # Manually layout the samples. # convert doesn't lay out the samples as pulse audio expects - samples = Array{Float32, 1}(size(data, 1) * size(data, 2)) + samples = Array{Float32, 1}(undef, size(data, 1) * size(data, 2)) idx = 1 for i = 1:size(data, 1) for j = 1:size(data, 2) @@ -108,7 +111,7 @@ function wavplay(data, fs) PA_STREAM_PLAYBACK, C_NULL, # Use the default device "wavplay", # description of stream - &ss, + Ref(ss), C_NULL, # Use default channel map C_NULL, # Use default buffering attributes C_NULL) # Ignore error code @@ -118,7 +121,7 @@ function wavplay(data, fs) write_ret = ccall((:pa_simple_write, LibPulseSimple), Cint, - (pa_simple, Ptr{Void}, Csize_t, Ptr{Cint}), + (pa_simple, Ptr{Cvoid}, Csize_t, Ptr{Cint}), s, samples, sizeof(samples), C_NULL) if write_ret != 0 error("pa_simple_write failed with $write_ret") @@ -131,6 +134,6 @@ function wavplay(data, fs) error("pa_simple_drain failed with $drain_ret") end - ccall((:pa_simple_free, LibPulseSimple), Void, (pa_simple,), s) + ccall((:pa_simple_free, LibPulseSimple), Cvoid, (pa_simple,), s) end end # module diff --git a/src/wavplay-unsupported.jl b/src/wavplay-unsupported.jl index 3784917..caf1132 100644 --- a/src/wavplay-unsupported.jl +++ b/src/wavplay-unsupported.jl @@ -1,5 +1,5 @@ # -*- mode: julia; -*- module WAVPlay -import WAV.wavplay -wavplay(data, fs) = warn("wavplay is not currently implemented on $OS_NAME") +import ..wavplay +wavplay(data, fs) = warn("wavplay is not currently implemented on $(Sys.KERNEL)") end # module diff --git a/test/runtests.jl b/test/runtests.jl index 97bd652..0b88fba 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,9 @@ ## -*-Julia-*- ## Test suite for Julia's WAV module import WAV -using Base.Test +using Compat.Test using Compat -using Compat.String +using Compat: AbstractDisplay, findall, occursin, repr, String, undef # These float array comparison functions are from dists.jl function absdiff(current::AbstractArray{T}, target::AbstractArray{T}) where T <: Real @@ -134,14 +134,14 @@ let end function testread(io, ::Type{T}, sz) where T <: Real - a = Array{T}(sz) + a = Array{T}(undef, sz) read!(io, a) return a end ## Test wavread and wavwrite ## Generate some wav files for writing and reading -for fs = (8000,11025,22050,44100,48000,96000,192000), nbits = (1,7,8,9,12,16,20,24,32,64), nsamples = convert(Array{Int}, [0; logspace(1, 4, 4)]), nchans = 1:4 +for fs = (8000,11025,22050,44100,48000,96000,192000), nbits = (1,7,8,9,12,16,20,24,32,64), nsamples = [0; 10 .^ (1:4)], nchans = 1:4 ## Test wav files ## The tolerance is based on the number of bits used to encode the file in wavwrite tol = 2.0 / (2.0^(nbits - 1)) @@ -320,7 +320,7 @@ end ## Test encoding 32 bit values for nchans = (1,2,4) - in_data_single = convert(Array{Float32}, reshape(linspace(-1.0, 1.0, 128), trunc(Int, 128 / nchans), nchans)) + in_data_single = convert(Array{Float32}, reshape(Compat.range(-1.0, stop=1.0, length=128), trunc(Int, 128 / nchans), nchans)) io = IOBuffer() WAV.wavwrite(in_data_single, io) @@ -355,7 +355,7 @@ end ## Test encoding 64 bit values for nchans = (1,2,4) - in_data_single = convert(Array{Float64}, reshape(linspace(-1.0, 1.0, 128), trunc(Int, 128 / nchans), nchans)) + in_data_single = convert(Array{Float64}, reshape(Compat.range(-1.0, stop=1.0, length=128), trunc(Int, 128 / nchans), nchans)) io = IOBuffer() WAV.wavwrite(in_data_single, io) @@ -389,7 +389,7 @@ for nchans = (1,2,4) end ### Test A-Law and Mu-Law -for nbits = (8, 16), nsamples = convert(Array{Int}, [0; logspace(1, 4, 4)]), nchans = 1:4, fmt=(WAV.WAVE_FORMAT_ALAW, WAV.WAVE_FORMAT_MULAW) +for nbits = (8, 16), nsamples = [0; 10 .^ (1:4)], nchans = 1:4, fmt=(WAV.WAVE_FORMAT_ALAW, WAV.WAVE_FORMAT_MULAW) fs = 8000.0 tol = 2.0 / (2.0^6) in_data = rand(nsamples, nchans) @@ -469,7 +469,7 @@ for nbits = (8, 16), nsamples = convert(Array{Int}, [0; logspace(1, 4, 4)]), nch end ### Test float formatting -for nbits = (32, 64), nsamples = convert(Array{Int}, [0; logspace(1, 4, 4)]), nchans = 1:2, fmt=(WAV.WAVE_FORMAT_IEEE_FLOAT) +for nbits = (32, 64), nsamples = [0; 10 .^ (1:4)], nchans = 1:2, fmt=(WAV.WAVE_FORMAT_IEEE_FLOAT) fs = 8000.0 tol = 1e-6 in_data = rand(nsamples, nchans) @@ -570,8 +570,8 @@ let seek(io, 0) data, fs, nbits, ext = WAV.wavread(io) - @test length(find(c -> c.id == :LIST, ext)) == length(in_chunks) - list_chunks = ext[find(c -> c.id == :LIST, ext)] + @test length(findall(c -> c.id == :LIST, ext)) == length(in_chunks) + list_chunks = ext[findall(c -> c.id == :LIST, ext)] for (c, i) in zip(list_chunks, in_chunks) @test c.id == i.id @test c.data == i.data @@ -619,11 +619,11 @@ let end ### WAVArray -struct TestHtmlDisplay <: Display +struct TestHtmlDisplay <: AbstractDisplay io::IOBuffer end function display(d::TestHtmlDisplay, mime::MIME"text/html", x) - print(d.io, reprmime(mime, x)) + print(d.io, repr(mime, x)) end let @@ -631,7 +631,7 @@ let wa = WAV.WAVArray(8000, sin.(1:256 * 8000.0 / 1024)); myio = IOBuffer() display(TestHtmlDisplay(myio), MIME"text/html"(), wa) - @test ismatch(r"audio controls", String(take!(copy(myio)))) + @test occursin(r"audio controls", String(take!(copy(myio)))) end ### playback