Skip to content

Commit

Permalink
More metaprogramming tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kunitoki committed Dec 4, 2024
1 parent bbcced0 commit 1f512d7
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 42 deletions.
77 changes: 37 additions & 40 deletions modules/juce_audio_devices/native/juce_AudioWorklet_emscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@
namespace juce
{

namespace
{

template <class T>
using hasAudioSampleFrameSamplesPerChannel = decltype(T::samplesPerChannel);

template <class T>
int getNumSamplesPerChannel (const T& frame)
{
if constexpr (isDetected<hasAudioSampleFrameSamplesPerChannel, T>)
return frame.samplesPerChannel;
else
return 128;
}

} // namespace

//==============================================================================
class AudioWorkletAudioIODevice final : public AudioIODevice
{
Expand Down Expand Up @@ -94,11 +111,9 @@ class AudioWorkletAudioIODevice final : public AudioIODevice
double sampleRate,
int bufferSizeSamples) override
{
yup::Logger::outputDebugString ("open");

if (sampleRate != getDefaultSampleRate())
if (sampleRate != getDefaultSampleRate() || bufferSizeSamples != getDefaultBufferSize())
{
lastError = "Browser audio outputs only support 44.1 kHz sample rate";
lastError = "Browser audio outputs only support 44.1 kHz sample rate and 128 samples buffer size.";
return lastError;
}

Expand All @@ -125,8 +140,6 @@ class AudioWorkletAudioIODevice final : public AudioIODevice

void close() override
{
yup::Logger::outputDebugString ("close");

stop();

if (isDeviceOpen)
Expand Down Expand Up @@ -155,17 +168,11 @@ class AudioWorkletAudioIODevice final : public AudioIODevice

void start (AudioIODeviceCallback* newCallback) override
{
yup::Logger::outputDebugString ("start 1");

if (! isDeviceOpen)
return;

yup::Logger::outputDebugString ("start 2");

if (isRunning)
{
yup::Logger::outputDebugString ("start 3");

if (newCallback != callback)
{
if (newCallback != nullptr)
Expand All @@ -182,40 +189,27 @@ class AudioWorkletAudioIODevice final : public AudioIODevice
}
else
{
yup::Logger::outputDebugString ("start 4");

callback = newCallback;
isRunning = emscripten_audio_context_state (context) == AUDIO_CONTEXT_STATE_RUNNING;

if (! isRunning && hasBeenActivatedAlreadyByUser)
{
yup::Logger::outputDebugString ("start 5");

emscripten_resume_audio_context_sync (context);
isRunning = emscripten_audio_context_state (context) == AUDIO_CONTEXT_STATE_RUNNING;
}

firstCallback = true;

yup::Logger::outputDebugString ("start 6");

if (callback != nullptr)
{
if (isRunning)
{
yup::Logger::outputDebugString ("start 7");
callback->audioDeviceAboutToStart (this);
}
}

yup::Logger::outputDebugString ("start 8");
}
}

void stop() override
{
yup::Logger::outputDebugString ("stop");

AudioIODeviceCallback* oldCallback = nullptr;

if (callback != nullptr)
Expand Down Expand Up @@ -260,12 +254,12 @@ class AudioWorkletAudioIODevice final : public AudioIODevice
}

int getOutputLatencyInSamples() override
{ /* TODO */
{
return 0;
}

int getInputLatencyInSamples() override
{ /* TODO */
{
return 0;
}

Expand All @@ -279,8 +273,6 @@ class AudioWorkletAudioIODevice final : public AudioIODevice

void audioThreadInitialized()
{
yup::Logger::outputDebugString ("audioThreadInitialized");

WebAudioWorkletProcessorCreateOptions opts =
{
.name = audioWorkletTypeName
Expand All @@ -292,8 +284,6 @@ class AudioWorkletAudioIODevice final : public AudioIODevice

void audioWorkletProcessorCreated()
{
yup::Logger::outputDebugString ("audioWorkletProcessorCreated");

int outputChannelCounts[1] = { actualNumberOfOutputs };
EmscriptenAudioWorkletNodeCreateOptions options =
{
Expand All @@ -307,7 +297,7 @@ class AudioWorkletAudioIODevice final : public AudioIODevice
context, audioWorkletTypeName, &options, renderAudioCallback, this);

// Connect it to audio context destination
//emscripten_audio_node_connect (audioWorkletNode, context, 0, 0);
// emscripten_audio_node_connect (audioWorkletNode, context, 0, 0);
EM_ASM ({
emscriptenGetAudioObject ($0).connect (emscriptenGetAudioObject ($1).destination);
}, audioWorkletNode, context);
Expand All @@ -319,10 +309,19 @@ class AudioWorkletAudioIODevice final : public AudioIODevice
int numOutputs, AudioSampleFrame* outputs,
int numParams, const AudioParamFrame* params)
{
const int audioFrames = 128;
const int samplesPerChannel = [&]
{
if (numOutputs > 0)
return getNumSamplesPerChannel (outputs[0]);

else if (numInputs > 0)
return getNumSamplesPerChannel (inputs[0]);

return 128;
}();

// check for xruns
calculateXruns (audioFrames);
calculateXruns (samplesPerChannel);

ScopedLock lock (callbackLock);

Expand All @@ -334,17 +333,17 @@ class AudioWorkletAudioIODevice final : public AudioIODevice

// Setup channelOutBuffers (assume a single worklet output)
for (int ch = 0; ch < actualNumberOfOutputs; ++ch)
channelOutBuffer[ch] = &(outputs[0].data[ch * 128]); // outputs[0].samplesPerChannel / outputs[0].quantumSize
channelOutBuffer[ch] = &(outputs[0].data[ch * samplesPerChannel]);

callback->audioDeviceIOCallbackWithContext (channelInBuffer.getData(),
actualNumberOfInputs,
channelOutBuffer.getData(),
actualNumberOfOutputs,
audioFrames,
samplesPerChannel,
{});
}

audioFramesElapsed += audioFrames;
audioFramesElapsed += samplesPerChannel;
}

return EM_TRUE; // keep going !
}
Expand All @@ -353,8 +352,6 @@ class AudioWorkletAudioIODevice final : public AudioIODevice
{
if (emscripten_audio_context_state (context) != AUDIO_CONTEXT_STATE_RUNNING)
{
yup::Logger::outputDebugString ("canvasClick");

emscripten_resume_audio_context_sync (context);

isRunning = true;
Expand Down
57 changes: 57 additions & 0 deletions modules/juce_core/misc/juce_MetaProgramming.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,67 @@
namespace juce
{

//==============================================================================
template <bool Value, auto... Args>
inline constexpr bool dependentBoolValue = Value;

template <auto... Args>
inline constexpr bool dependentFalse = dependentBoolValue<false, Args...>;

//==============================================================================
struct NoneSuch
{
NoneSuch() = delete;
~NoneSuch() = delete;
NoneSuch(NoneSuch const&) = delete;
void operator=(NoneSuch const&) = delete;
NoneSuch(NoneSuch&&) = delete;
void operator=(NoneSuch&&) = delete;
};

namespace detail {
template <class Default, class AlwaysVoid, template <class...> class Op, class... Args>
struct Detector
{
using ValueType = std::false_type;
using Type = Default;
};

template <class Default, template <class...> class Op, class... Args>
struct Detector<Default, std::void_t<Op<Args...>>, Op, Args...>
{
using ValueType = std::true_type;
using Type = Op<Args...>;
};

template <template <class...> class Op, class... Args>
using isDetected = typename Detector<NoneSuch, void, Op, Args...>::ValueType;

template <class Default, template <class...> class Op, class... Args>
using detectedOr = Detector<Default, void, Op, Args...>;
} // namespace detail

template <template <class...> class Op, class... Args>
inline constexpr bool isDetected = detail::isDetected<Op, Args...>::value;

template <template <class...> class Op, class... Args>
using detectedType = typename detail::Detector<NoneSuch, void, Op, Args...>::Type;

template <class Default, template <class...> class Op, class... Args>
using detectedOr = typename detail::detectedOr<Default, Op, Args...>::Type;

namespace detail {
template <class Expected, template <class...> class Op, class... Args>
using isDetectedExact = std::is_same<Expected, detectedType<Op, Args...>>;

template <class To, template <class...> class Op, class... Args>
using isDetectedConvertible = std::is_convertible<detectedType<Op, Args...>, To>;
} // namespace detail

template <class Expected, template <class...> class Op, class... Args>
inline constexpr bool isDetectedExact = detail::isDetectedExact<Expected, Op, Args...>::value;

template <class To, template <class...> class Op, class... Args>
inline constexpr bool isDetectedConvertible = detail::isDetectedConvertible<To, Op, Args...>::value;

} // namespace juce
3 changes: 1 addition & 2 deletions modules/juce_core/native/juce_Files_wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,7 @@ bool Process::openDocument (const String& fileName, const String& parameters)
document.body.appendChild (elem);
elem.click();
document.body.removeChild (elem);
},
cmdString.toRawUTF8());
}, cmdString.toRawUTF8());

return true;
}
Expand Down
94 changes: 94 additions & 0 deletions tests/juce_core/juce_MetaProgramming.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
==============================================================================
This file is part of the YUP library.
Copyright (c) 2024 - [email protected]
YUP is an open source library subject to open-source licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
to use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/

#include <gtest/gtest.h>

#include <juce_core/juce_core.h>

#include <string>

using namespace juce;

namespace {

struct HaveIt
{
bool existingMethod(int, float);
bool existingMethod2(int, float, int);

std::string field;
};

struct DontHaveIt
{
uint32_t field;
};

template <class T>
using hasExistingMethod = decltype(&T::existingMethod);

template <class T>
using hasExistingMethod2 = decltype(&T::existingMethod2);

template <class T>
using hasField = decltype(T::field);

} // namespace

TEST (MetaProgrammingTests, DependentBoolValue)
{
static_assert (dependentBoolValue<true>);
static_assert (! dependentBoolValue<false>);
static_assert (! dependentFalse<>);
}

TEST (MetaProgrammingTests, IsDetected)
{
static_assert (isDetected<hasExistingMethod, HaveIt>);
static_assert (! isDetected<hasExistingMethod, DontHaveIt>);
}

TEST (MetaProgrammingTests, IsDetectedExact)
{
static_assert (isDetectedExact<decltype(&HaveIt::existingMethod), hasExistingMethod, HaveIt>);
static_assert (! isDetectedExact<decltype(&HaveIt::existingMethod), hasExistingMethod2, HaveIt>);
static_assert (! isDetectedExact<decltype(&HaveIt::existingMethod), hasExistingMethod, DontHaveIt>);
}

TEST (MetaProgrammingTests, IsDetectedConvertible)
{
static_assert (isDetectedConvertible<std::string_view, hasField, HaveIt>);
static_assert (! isDetectedConvertible<std::size_t, hasField, HaveIt>);
static_assert (! isDetectedConvertible<std::string_view, hasField, DontHaveIt>);
static_assert (isDetectedConvertible<std::size_t, hasField, DontHaveIt>);
}

TEST (MetaProgrammingTests, DetectedType)
{
static_assert (std::is_same_v<detectedType<hasExistingMethod, HaveIt>, decltype(&HaveIt::existingMethod)>);
static_assert (std::is_same_v<detectedType<hasExistingMethod, DontHaveIt>, yup::NoneSuch>);
}

TEST (MetaProgrammingTests, DetectedOr)
{
static_assert (std::is_same_v<detectedOr<int, hasExistingMethod, HaveIt>, decltype(&HaveIt::existingMethod)>);
static_assert (std::is_same_v<detectedOr<int, hasExistingMethod, DontHaveIt>, int>);
}

0 comments on commit 1f512d7

Please sign in to comment.