AudioKit - Drawing full waveform of file - swift

I've been going through documentation looking for an answer for this. I see that AudioKit can draw waveforms for in realtime as you record or playback, but I was wondering if you could load in a file and it draw the waveform in full so I can see the whole file's waveform without playing it back.
Any help would be greatly appreciated. Even just a pointer to what object I should look into.

You can also use the Objective C EZAudioPlot class which is in AudioKitUI:
let file = EZAudioFile(url: url)
guard let data = file?.getWaveformData() else { return }
let waveform = EZAudioPlot()
addSubview( waveform )
waveform.frame = NSMakeRect(0, 0, 200, 20)
waveform.plotType = EZPlotType.buffer
waveform.shouldFill = true
waveform.shouldMirror = true
waveform.color = NSColor.black
waveform.updateBuffer( data.buffers[0], withBufferSize: data.bufferSize )
I haven't benchmarked the two methods, but the plot version is very fast for longer files. For stereo files, make two stacked plots using buffers[0] and [1]

there is a waveform that is drawn in the tables playground example here: http://audiokit.io/playgrounds/Basics/Tables/
Basically:
let file = try AKAudioFile(readFileName: "drumloop.wav")
let fileTable = AKTable(file: file)
...UI set up...
addView(AKTableView(fileTable))

Related

Problem with precision printing from Swift & MacOS

I am creating an app to create and print patterns in real size. It works great on the screen since I can get screenwidth and ppi from GeometryReader and CGDisplayScreenSize. However, you can't cut a pattern out of the screen. I need to do it on normal printers and I need precision down to 0.004" or 0.1mmm.
My only real options for formatting are PDF and EPS. My first read on PDF indicated that 72dpi is a reliable dpi for PDF. Deeper reading and experimentation have shown me that is not the case. Experimentation has shown me that 75dpi produces better results. It's no coincidence that 300, 600 and 1200 are multiples of 75.
I can't find a way in PDFKit to set a resolution. Can anybody provide guidance or alternative approaches? The way I create the PDF right now is;
let output = URL(fileURLWithPath: "PaperPattern.pdf", relativeTo: FileManager.default.temporaryDirectory)
let page = PDFPage()
if let gc = CGContext(output as CFURL, mediaBox: nil, nil)
{
gc.beginPDFPage(nil)
drawPattern(path: gc, hp: hp) //hp contains a series of points based on a fixed dpi resolution
page.draw(with: .mediaBox, to: gc)
gc.endPDFPage()
gc.closePDF()
}
let doc: PDFDocument = PDFDocument(url: output)!
doc.insert(page, at: 0)
doc.removePage(at: 0)
return doc

Assimp: Load .blend file with correct rotation

I have a simple .blend level which looks like it's flipped when importing with Assimp:
Blender uses Z as its Up vector, my engine uses Y. There are no export/save options in Blender that allows me to set that in .blend
Afaik Assimp should handle this for me. Here's how I'm loading the model, converting it to a runtime format for my engine:
// vertices, normals and uvs
{
u32 VertexCount = SrcMesh->mNumVertices;
u32 ByteCount = VertexCount * sizeof(f32) * 3;
DstMesh->VerticesCount = VertexCount;
DstMesh->Vertices = new vec[VertexCount];
memcpy(DstMesh->Vertices, SrcMesh->mVertices, ByteCount);
FAIL_IF_NOT(SrcMesh->mNormals, "Model doesn't have normals");
DstMesh->Normals = new vec[VertexCount];
memcpy(DstMesh->Normals, SrcMesh->mNormals, ByteCount);
FAIL_IF_NOT(SrcMesh->mTextureCoords[0], "Model doesn't have UVs");
DstMesh->UVs = new vec[VertexCount];
memcpy(DstMesh->UVs, SrcMesh->mTextureCoords[0], ByteCount);
}
// faces
{
DstMesh->FacesCount = SrcMesh->mNumFaces;
DstMesh->Faces = new mesh_face[DstMesh->FacesCount];
For(i32, F, DstMesh->FacesCount)
{
aiFace *SrcFace = &SrcMesh->mFaces[F];
mesh_face *DstFace = &DstMesh->Faces[F];
FAIL_IF_NOT(SrcFace->mNumIndices == 3, "Model is not triangulated. Face index %d has %d indices", F, SrcFace->mNumIndices);
memcpy(DstFace->Indices, SrcFace->mIndices, 3 * sizeof(u32));
}
}
Note I also tried finding the node that mesh's belong to, and transforming the vertices/normals by that node's final transform (by final I mean I recurse all the node's parents and apply their transforms and finally the node's local transform) -- But that didn't do anything because the mesh has no parent.
My questions are:
What is the right way to load .blend files?
Shouldn't Assimp be handling this on its own on import?
or do I need to manually rotate/flip things?
Here's the referenced model: https://www.dropbox.com/s/hwsyfyqip5pqhrh/StusRoom.blend?dl=0
Thanks for any help!
I figured out that Blender expect .glb files to be in XZ-Y axis order, so perhaps this is true for .blend files as well.
Regarding the AssImp - I was dealing with this issue myself on my work, and I figured out that the problem is the AssImp - check the link: https://github.com/assimp/assimp/issues/849 (as you can see this is still open, even though it's over 5 years old now)
I believe you need to figure out some workaround to somehow manually rotate the file, since assimp will rotate it for you.
Hope it helps! Best regards

Accessing Float samples of AVAudioPCMBuffer for processing

I am trying to do some computation on the raw PCM samples of a mp3 files I'm playing with an AVAudioEngine graph. I have a closure every 44100 samples that provides an AVAudioPCMBuffer. It has a property channelData of type UnsafePointer<UnsafeMutablePointer<Float>>?. I have not worked with pointers in Swift 3 and so I'm unclear how to access these Float values.
I have the following code but there are many issues:
audioPlayerNode.installTap(onBus: 0,
bufferSize: 1024,
format: audioPlayerNode.outputFormat(forBus: 0)) { (pcmBuffer, time) in
let numChans = Int(pcmBuffer.format.channelCount)
let frameLength = pcmBuffer.frameLength
if let chans = pcmBuffer.floatChannelData?.pointee {
for a in 0..<numChans {
let samples = chans[a]// samples is type Float. should be pointer to Floats.
for b in 0..<flength {
print("sample: \(b)") // should be samples[b] but that gives error as "samples" is Float
}
}
}
For instance, how do I iterate through the UnsafeMutablePointer<Floats which are N float pointers where N is the number of channels in the buffer. I could not find discussion on accessing buffer samples in the Apple Docs on this Class.
I think the main problem is let samples = chans[a]. Xcode says chans is of type UnsafeMutablePointer<Float>. But that should be NumChannels worth of those pointers. Which is why I use a in 0..<numChans to subscript it. Yet I get just Float when I do.
EDIT:
hm, seems using chans.advanced(by: a) instead of subscripting fixed things
Here is what I've found:
let arraySize = Int(buffer.frameLength)
let samples = Array(UnsafeBufferPointer(start: buffer.floatChannelData![0], count:arraySize))
This is assuming buffer is the name of your AVAudioPCMBuffer.
This way you can avoid pointers, which is likely much simpler. Now you can actually search through the data using a for loop.

AVAudioPCMBuffer built programmatically, not playing back in stereo

I'm trying to fill an AVAudioPCMBuffer programmatically in Swift to build a metronome. This is the first real app I'm trying to build, so it's also my first audio app. Right now I'm experimenting with different frameworks and methods of getting the metronome looping accurately.
I'm trying to build an AVAudioPCMBuffer with the length of a measure/bar so that I can use the .Loops option of the AVAudioPlayerNode's scheduleBuffer method. I start by loading my file(2 ch, 44100 Hz, Float32, non-inter, *.wav and *.m4a both have same issue) into a buffer, then copying that buffer frame by frame separated by empty frames into the barBuffer. The loop below is how I'm accomplishing this.
If I schedule the original buffer to play, it will play back in stereo, but when I schedule the barBuffer, I only get the left channel. As I said I'm a beginner at programming, and have no experience with audio programming, so this might be my lack of knowledge on 32 bit float channels, or on this data type UnsafePointer<UnsafeMutablePointer<float>>. When I look at the floatChannelData property in swift, the description makes it sound like this should be copying two channels.
var j = 0
for i in 0..<Int(capacity) {
barBuffer.floatChannelData.memory[j] = buffer.floatChannelData.memory[i]
j += 1
}
j += Int(silenceLengthInSamples)
// loop runs 4 times for 4 beats per bar.
edit: I removed the glaring mistake i += 1, thanks to hotpaw2. The right channel is still missing when barBuffer is played back though.
Unsafe pointers in swift are pretty weird to get used to.
floatChannelData.memory[j] only accesses the first channel of data. To access the other channel(s), you have a couple choices:
Using advancedBy
// Where current channel is at 0
// Get a channel pointer aka UnsafePointer<UnsafeMutablePointer<Float>>
let channelN = floatChannelData.advancedBy( channelNumber )
// Get channel data aka UnsafeMutablePointer<Float>
let channelNData = channelN.memory
// Get first two floats of channel channelNumber
let floatOne = channelNData.memory
let floatTwo = channelNData.advancedBy(1).memory
Using Subscript
// Get channel data aka UnsafeMutablePointer<Float>
let channelNData = floatChannelData[ channelNumber ]
// Get first two floats of channel channelNumber
let floatOne = channelNData[0]
let floatTwo = channelNData[1]
Using subscript is much clearer and the step of advancing and then manually
accessing memory is implicit.
For your loop, try accessing all channels of the buffer by doing something like this:
for i in 0..<Int(capacity) {
for n in 0..<Int(buffer.format.channelCount) {
barBuffer.floatChannelData[n][j] = buffer.floatChannelData[n][i]
}
}
Hope this helps!
This looks like a misunderstanding of Swift "for" loops. The Swift "for" loop automatically increments the "i" array index. But you are incrementing it again in the loop body, which means that you end up skipping every other sample (the Right channel) in your initial buffer.

Mosaic images with Matlab

I have a question about how can I do something. I have a folder with different images (each image has 3 bands). For example.
Img_244_234_1_1.tif
Img_244_234_1_2.tif
Img_250_234_1_1.tif
Img_250_234_1_2.tif
What I need to do is to mosaic the images by name (for example, all the number 244, 250...). Now, I'm doing it manually in that way:
image1 = imread('C:\Prueba\Img_244_234_1_1.tif','tif');
image2 = imread('C:\Prueba\Img_244_234_1_2.tif','tif');
image3 = imread('C:\Prueba\Img_250_234_1_1.tif','tif');
image4 = imread('C:\Prueba\Img_250_234_1_2.tif','tif');
image_result1 = cat(2,image1,image2);
image_result1 = cat(2,image1,image2);
How can I automatize using the date number (244,250...) which always it's in the same output name position?
Really appreciate any suggestion.
You can use loops (like for x=[244,255]) and the concatenation of strings: ['C:\Prueba\Img_' x '_234_1_1.tif'] will evaluate to `'C:\Prueba\Img_244_234_1_1.tif' if x was 244.
If your file name is well organized, then the following code should work.
cd('C:\Prueba\');
files = dir('*.tif');
for i=1:2:numel(files)
image1 = imread(files(i).name);
image2 = imread(files(i+1).name);
image_result = cat(2,image1,image2);
end