Skip to content

Commit

Permalink
Fix (#122)
Browse files Browse the repository at this point in the history
* Fix

* Format
  • Loading branch information
umireon authored Jul 22, 2024
1 parent b1e8146 commit a292397
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include <stdint.h>
#include <stdio.h>

#define INT16_TO_FLOAT(x) (x * 3.0517578125e-05)

Expand Down
106 changes: 82 additions & 24 deletions FragmentedRecordWriterTests/AudioResamplerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ private let documentsURL = FileManager.default.urls(for: .documentDirectory, in:

// swiftlint:disable function_body_length
final class AudioResamplerTests: XCTestCase {
let className = "AudioResamplerTests"

func getOutputDirectoryURL(name: String) throws -> URL {
let outputDirectoryURL = documentsURL.appending(path: name)

Expand All @@ -20,42 +22,73 @@ final class AudioResamplerTests: XCTestCase {
return outputDirectoryURL
}

func testAsIs() async throws {
try await run(
name: "AudioResamplerTests_testAsIs",
inputSampleRate: 48_000,
outputSampleRate: 48_000
)
func testCopyInt16() async throws {
for numChannels in [1, 2] {
for isSwapped in [false, true] {
try await run(
name: "\(className)_testCopyInt16_\(numChannels)_\(isSwapped)",
inputSampleRate: 48_000,
outputSampleRate: 48_000,
numChannels: numChannels,
bytesPerSample: 2,
isSwapped: isSwapped
)
}
}
}

func testUpsamplingBy2() async throws {
try await run(
name: "AudioResamplerTests_testUpsamplingBy2",
name: "\(className)_\(#function)",
inputSampleRate: 24_000,
outputSampleRate: 48_000
outputSampleRate: 48_000,
numChannels: 2,
bytesPerSample: 2,
isSwapped: false
)
}

func testUpsamplingBy6() async throws {
try await run(
name: "AudioResamplerTests_testUpsamplingBy6",
name: "\(className)_\(#function)",
inputSampleRate: 8_000,
outputSampleRate: 48_000
outputSampleRate: 48_000,
numChannels: 2,
bytesPerSample: 2,
isSwapped: false
)
}

func testUpsamplingFrom44100To48000() async throws {
try await run(
name: "AudioResamplerTests_testUpsamplingFrom44100To48000",
name: "\(className)_\(#function)",
inputSampleRate: 44_100,
outputSampleRate: 48_000
outputSampleRate: 48_000,
numChannels: 2,
bytesPerSample: 2,
isSwapped: false
)
}

private func run(name: String, inputSampleRate: Int, outputSampleRate: Int) async throws {
private func run(
name: String,
inputSampleRate: Int,
outputSampleRate: Int,
numChannels: Int,
bytesPerSample: Int,
isSwapped: Bool
) async throws {
let outputDirectoryURL = try getOutputDirectoryURL(name: name)
print("Output directory is \(outputDirectoryURL.path())")

let dummyAppAudioGenerator = try DummyAudioGenerator(
sampleRate: inputSampleRate,
numChannels: numChannels,
bytesPerSample: bytesPerSample,
isSwapped: isSwapped,
initialPTS: .zero
)

let audioResampler = try AudioResampler(outputSampleRate: outputSampleRate)

let audioTranscoder = try RealtimeAudioTranscoder(
Expand All @@ -70,20 +103,45 @@ final class AudioResamplerTests: XCTestCase {
sourceFormatHint: audioTranscoder.outputFormatDesc
)

let dummyAppAudioGenerator = try DummyAudioGenerator(
sampleRate: inputSampleRate,
initialPTS: CMTime.zero
)

for _ in 0..<inputSampleRate * 10 / 1024 {
let dummyAudioFrame = dummyAppAudioGenerator.generateNextAudioFrame()

try audioResampler.append(
stereoInt16Buffer: dummyAudioFrame.data,
numInputSamples: Int(dummyAudioFrame.audioBufferList.mBuffers.mDataByteSize / 4),
inputSampleRate: inputSampleRate,
pts: dummyAudioFrame.pts
)
if bytesPerSample == 1 {
} else if bytesPerSample == 2 {
if numChannels == 1 {
if isSwapped {
try audioResampler.append(
monoInt16BufferWithSwap: dummyAudioFrame.data.assumingMemoryBound(to: Int16.self),
numInputSamples: dummyAudioFrame.numSamples,
inputSampleRate: inputSampleRate,
pts: dummyAudioFrame.pts
)
} else {
try audioResampler.append(
monoInt16Buffer: dummyAudioFrame.data.assumingMemoryBound(to: Int16.self),
numInputSamples: dummyAudioFrame.numSamples,
inputSampleRate: inputSampleRate,
pts: dummyAudioFrame.pts
)
}
} else if numChannels == 2 {
if isSwapped {
try audioResampler.append(
stereoInt16BufferWithSwap: dummyAudioFrame.data.assumingMemoryBound(to: Int16.self),
numInputSamples: dummyAudioFrame.numSamples,
inputSampleRate: inputSampleRate,
pts: dummyAudioFrame.pts
)
} else {
try audioResampler.append(
stereoInt16Buffer: dummyAudioFrame.data.assumingMemoryBound(to: Int16.self),
numInputSamples: dummyAudioFrame.numSamples,
inputSampleRate: inputSampleRate,
pts: dummyAudioFrame.pts
)
}
}
}
let audioResamplerFrame = audioResampler.getCurrentFrame()

let numInputSamples = audioResamplerFrame.numSamples
Expand Down
62 changes: 52 additions & 10 deletions FragmentedRecordWriterTests/DummyAudioGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ import CoreAudio
import CoreMedia
import Foundation

enum DummyAudioGeneratorError: Error {
case initializingParameterNotSupported(numChannels: Int, bytesPerSample: Int)
}

struct DummyAudioGeneratorFrame {
let numSamples: Int
let pts: CMTime
let data: UnsafeMutablePointer<Int16>
let data: UnsafeMutableRawPointer
let audioBufferList: AudioBufferList
}

class DummyAudioGenerator {
public let formatDesc: CMFormatDescription

private let sampleRate: Int
private let bytesPerSample: Int
private let initialPTS: CMTime

private var state: DummyAudioGeneratorState
Expand All @@ -22,31 +27,68 @@ class DummyAudioGenerator {
private let numChannels = 2
private let dataByteSize = 4096

init(sampleRate: Int, initialPTS: CMTime) throws {
init(
sampleRate: Int,
numChannels: Int,
bytesPerSample: Int,
isSwapped: Bool,
initialPTS: CMTime
) throws {
guard [1, 2].contains(numChannels) else {
throw DummyAudioGeneratorError.initializingParameterNotSupported(
numChannels: numChannels,
bytesPerSample: bytesPerSample
)
}

let formatFlags: AudioFormatFlags
if bytesPerSample == 1 {
formatFlags = kAudioFormatFlagIsPacked
} else if bytesPerSample == 2 {
formatFlags =
kAudioFormatFlagIsSignedInteger | (isSwapped ? kAudioFormatFlagIsBigEndian : 0)
| kAudioFormatFlagIsPacked
} else {
throw DummyAudioGeneratorError.initializingParameterNotSupported(
numChannels: numChannels,
bytesPerSample: bytesPerSample
)
}

let bytesPerFrame = bytesPerSample * numChannels

formatDesc = try CMFormatDescription(
audioStreamBasicDescription: AudioStreamBasicDescription(
mSampleRate: Float64(sampleRate),
mFormatID: kAudioFormatLinearPCM,
mFormatFlags: kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked,
mBytesPerPacket: 4,
mFormatFlags: formatFlags,
mBytesPerPacket: UInt32(bytesPerFrame),
mFramesPerPacket: 1,
mBytesPerFrame: 4,
mChannelsPerFrame: 2,
mBitsPerChannel: 16,
mBytesPerFrame: UInt32(bytesPerFrame),
mChannelsPerFrame: UInt32(numChannels),
mBitsPerChannel: UInt32(bytesPerSample * 8),
mReserved: 0
))
)
)

self.sampleRate = sampleRate
self.bytesPerSample = bytesPerSample
self.initialPTS = initialPTS

let dataSize = numSamples * numChannels * bytesPerSample * 16

self.state = DummyAudioGeneratorState(
data: .allocate(capacity: numSamples * numChannels * 10),
data: .allocate(byteCount: dataSize, alignment: 8),
numSamples: numSamples,
numChannels: numChannels,
bytesPerSample: bytesPerSample,
isSwapped: isSwapped,
t: 0,
tincr: 2 * Double.pi * 330.0 / Double(sampleRate),
tincr2: 2 * Double.pi * 330.0 / Double(sampleRate) / Double(sampleRate)
)

state.data.initializeMemory(as: UInt8.self, repeating: 0, count: dataSize)
}

func generateNextAudioFrame() -> DummyAudioGeneratorFrame {
Expand All @@ -65,7 +107,7 @@ class DummyAudioGenerator {
mNumberBuffers: 1,
mBuffers: AudioBuffer(
mNumberChannels: UInt32(numChannels),
mDataByteSize: UInt32(dataByteSize),
mDataByteSize: UInt32(numSamples * numChannels * bytesPerSample),
mData: state.data
)
)
Expand Down
7 changes: 6 additions & 1 deletion FragmentedRecordWriterTests/FragmentedAudioWriterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ final class FragmentedAudioWriterTests: XCTestCase {
)

let dummyAppAudioGenerator = try DummyAudioGenerator(
sampleRate: sampleRate, initialPTS: CMTime.zero)
sampleRate: sampleRate,
numChannels: 2,
bytesPerSample: 2,
isSwapped: false,
initialPTS: .zero
)

let audioWriter = try FragmentedAudioWriter(
outputDirectoryURL: outputDirectoryURL,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

#import "../FragmentedRecordWriter/FragmentedRecordWriter-Bridging-Header.h"

Expand All @@ -28,9 +28,11 @@ static inline void fillChromaPlane(uint8_t *__nonnull chromaData, long width,
}

struct DummyAudioGeneratorState {
int16_t *__nonnull data;
void *__nonnull data;
long numSamples;
long numChannels;
long bytesPerSample;
bool isSwapped;
double t;
double tincr;
double tincr2;
Expand All @@ -43,13 +45,44 @@ static inline void fillAudio(struct DummyAudioGeneratorState *state) {
double tincr = state->tincr;
double tincr2 = state->tincr2;

for (long i = 0; i < numSamples; i++) {
t += tincr;
tincr += tincr2;
int16_t value = sin(t) * 10000;
if (state->bytesPerSample == 1) {
uint8_t *buffer = state->data;

for (long j = 0; j < numChannels; j++) {
state->data[i * numChannels + j] = value;
for (long i = 0; i < numSamples; i++) {
t += tincr;
tincr += tincr2;
uint8_t value = sin(t) * 100 + 128;

for (long j = 0; j < numChannels; j++) {
buffer[i * numChannels + j] = value;
}
}
} else if (state->bytesPerSample == 2) {
if (state->isSwapped) {
for (long i = 0; i < numSamples; i++) {
uint8_t *buffer = state->data;

t += tincr;
tincr += tincr2;
int16_t value = sin(t) * 10000;

for (long j = 0; j < numChannels; j++) {
buffer[(i * numChannels + j) * 2 + 0] = *((uint8_t *)&value + 1);
buffer[(i * numChannels + j) * 2 + 1] = *((uint8_t *)&value + 0);
}
}
} else {
int16_t *buffer = state->data;

for (long i = 0; i < numSamples; i++) {
t += tincr;
tincr += tincr2;
int16_t value = sin(t) * 10000;

for (long j = 0; j < numChannels; j++) {
buffer[i * numChannels + j] = value;
}
}
}
}

Expand Down

0 comments on commit a292397

Please sign in to comment.