mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-08-24 10:14:13 -05:00
aja: Add audio channel selection to capture
Adapted audio repacker for 32-bit samples, with an ifdef to use SIMD or C version.
This commit is contained in:
@@ -41,7 +41,10 @@ target_sources(
|
||||
aja-widget-io.cpp
|
||||
aja-widget-io.hpp
|
||||
aja-card-manager.hpp
|
||||
aja-ui-props.hpp)
|
||||
aja-ui-props.hpp
|
||||
audio-repack.c
|
||||
audio-repack.h
|
||||
audio-repack.hpp)
|
||||
|
||||
target_link_libraries(aja PRIVATE OBS::libobs AJA::LibAJANTV2)
|
||||
|
||||
|
||||
+30
-13
@@ -13,12 +13,13 @@ SourceProps::SourceProps()
|
||||
pixelFormat{NTV2_FBF_INVALID},
|
||||
sdiTransport{SDITransport::SingleLink},
|
||||
sdi4kTransport{SDITransport4K::TwoSampleInterleave},
|
||||
audioNumChannels{8},
|
||||
audioSampleSize{4},
|
||||
audioSampleRate{48000},
|
||||
audioNumChannels{kDefaultAudioChannels},
|
||||
audioSampleSize{kDefaultAudioSampleSize},
|
||||
audioSampleRate{kDefaultAudioSampleRate},
|
||||
vpids{},
|
||||
autoDetect{false},
|
||||
deactivateWhileNotShowing{false}
|
||||
deactivateWhileNotShowing{false},
|
||||
swapFrontCenterLFE{false}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -30,12 +31,13 @@ SourceProps::SourceProps(NTV2DeviceID devID)
|
||||
pixelFormat{NTV2_FBF_INVALID},
|
||||
sdiTransport{SDITransport::SingleLink},
|
||||
sdi4kTransport{SDITransport4K::TwoSampleInterleave},
|
||||
audioNumChannels{8},
|
||||
audioSampleSize{4},
|
||||
audioSampleRate{48000},
|
||||
audioNumChannels{kDefaultAudioChannels},
|
||||
audioSampleSize{kDefaultAudioSampleSize},
|
||||
audioSampleRate{kDefaultAudioSampleRate},
|
||||
vpids{},
|
||||
autoDetect{false},
|
||||
deactivateWhileNotShowing{false}
|
||||
deactivateWhileNotShowing{false},
|
||||
swapFrontCenterLFE{false}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,6 +56,7 @@ SourceProps::SourceProps(const SourceProps &props)
|
||||
vpids = props.vpids;
|
||||
autoDetect = props.autoDetect;
|
||||
deactivateWhileNotShowing = props.deactivateWhileNotShowing;
|
||||
swapFrontCenterLFE = props.swapFrontCenterLFE;
|
||||
}
|
||||
|
||||
SourceProps::SourceProps(SourceProps &&props)
|
||||
@@ -71,6 +74,7 @@ SourceProps::SourceProps(SourceProps &&props)
|
||||
vpids = props.vpids;
|
||||
autoDetect = props.autoDetect;
|
||||
deactivateWhileNotShowing = props.deactivateWhileNotShowing;
|
||||
swapFrontCenterLFE = props.swapFrontCenterLFE;
|
||||
}
|
||||
|
||||
void SourceProps::operator=(const SourceProps &props)
|
||||
@@ -88,6 +92,7 @@ void SourceProps::operator=(const SourceProps &props)
|
||||
vpids = props.vpids;
|
||||
autoDetect = props.autoDetect;
|
||||
deactivateWhileNotShowing = props.deactivateWhileNotShowing;
|
||||
swapFrontCenterLFE = props.swapFrontCenterLFE;
|
||||
}
|
||||
|
||||
void SourceProps::operator=(SourceProps &&props)
|
||||
@@ -105,6 +110,7 @@ void SourceProps::operator=(SourceProps &&props)
|
||||
vpids = props.vpids;
|
||||
autoDetect = props.autoDetect;
|
||||
deactivateWhileNotShowing = props.deactivateWhileNotShowing;
|
||||
swapFrontCenterLFE = props.swapFrontCenterLFE;
|
||||
}
|
||||
|
||||
bool SourceProps::operator==(const SourceProps &props)
|
||||
@@ -120,7 +126,8 @@ bool SourceProps::operator==(const SourceProps &props)
|
||||
audioNumChannels == props.audioNumChannels &&
|
||||
audioSampleSize == props.audioSampleSize &&
|
||||
audioSampleRate == props.audioSampleRate &&
|
||||
deactivateWhileNotShowing == props.deactivateWhileNotShowing);
|
||||
deactivateWhileNotShowing == props.deactivateWhileNotShowing &&
|
||||
swapFrontCenterLFE == props.swapFrontCenterLFE);
|
||||
}
|
||||
|
||||
bool SourceProps::operator!=(const SourceProps &props)
|
||||
@@ -196,9 +203,19 @@ audio_format SourceProps::AudioFormat() const
|
||||
|
||||
speaker_layout SourceProps::SpeakerLayout() const
|
||||
{
|
||||
if (audioNumChannels == 2)
|
||||
if (audioNumChannels == 1)
|
||||
return SPEAKERS_MONO;
|
||||
else if (audioNumChannels == 2)
|
||||
return SPEAKERS_STEREO;
|
||||
// NTV2 is always at least 8ch on modern boards
|
||||
else if (audioNumChannels == 3)
|
||||
return SPEAKERS_2POINT1;
|
||||
else if (audioNumChannels == 4)
|
||||
return SPEAKERS_4POINT0;
|
||||
else if (audioNumChannels == 5)
|
||||
return SPEAKERS_4POINT1;
|
||||
else if (audioNumChannels == 6)
|
||||
return SPEAKERS_5POINT1;
|
||||
// NTV2 card is always set to at least 8ch
|
||||
return SPEAKERS_7POINT1;
|
||||
}
|
||||
|
||||
@@ -213,8 +230,8 @@ OutputProps::OutputProps(NTV2DeviceID devID)
|
||||
pixelFormat{NTV2_FBF_INVALID},
|
||||
sdi4kTransport{SDITransport4K::TwoSampleInterleave},
|
||||
audioNumChannels{8},
|
||||
audioSampleSize{4},
|
||||
audioSampleRate{48000}
|
||||
audioSampleSize{kDefaultAudioSampleSize},
|
||||
audioSampleRate{kDefaultAudioSampleRate}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
uint32_t audioSampleRate;
|
||||
bool autoDetect;
|
||||
bool deactivateWhileNotShowing;
|
||||
bool swapFrontCenterLFE;
|
||||
};
|
||||
|
||||
class OutputProps {
|
||||
|
||||
@@ -152,7 +152,7 @@ void Routing::StartSourceAudio(const SourceProps &props, CNTV2Card *card)
|
||||
audioSys, NTV2InputSourceToAudioSource(inputSrc),
|
||||
NTV2InputSourceToEmbeddedAudioInput(inputSrc));
|
||||
|
||||
card->SetNumberAudioChannels(props.audioNumChannels, audioSys);
|
||||
card->SetNumberAudioChannels(kDefaultAudioChannels, audioSys);
|
||||
card->SetAudioRate(props.AudioRate(), audioSys);
|
||||
card->SetAudioBufferSize(NTV2_AUDIO_BUFFER_BIG, audioSys);
|
||||
|
||||
@@ -188,7 +188,7 @@ void Routing::StartSourceAudio(const SourceProps &props, CNTV2Card *card)
|
||||
|
||||
for (int a = 0; a < NTV2DeviceGetNumAudioSystems(card->GetDeviceID());
|
||||
a++) {
|
||||
card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_OFF,
|
||||
card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_ON,
|
||||
NTV2AudioSystem(a));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,30 @@
|
||||
#endif
|
||||
#define NTV2_AUDIOSIZE_MAX (401 * 1024)
|
||||
|
||||
static constexpr int kDefaultAudioCaptureChannels = 2;
|
||||
|
||||
static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format,
|
||||
bool swap = false)
|
||||
{
|
||||
switch (format) {
|
||||
case SPEAKERS_STEREO:
|
||||
return repack_mode_8to2ch;
|
||||
case SPEAKERS_2POINT1:
|
||||
return repack_mode_8to3ch;
|
||||
case SPEAKERS_4POINT0:
|
||||
return repack_mode_8to4ch;
|
||||
case SPEAKERS_4POINT1:
|
||||
return swap ? repack_mode_8to5ch_swap : repack_mode_8to5ch;
|
||||
case SPEAKERS_5POINT1:
|
||||
return swap ? repack_mode_8to6ch_swap : repack_mode_8to6ch;
|
||||
case SPEAKERS_7POINT1:
|
||||
return swap ? repack_mode_8ch_swap : repack_mode_8ch;
|
||||
default:
|
||||
assert(false && "No repack requested");
|
||||
return (audio_repack_mode_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
AJASource::AJASource(obs_source_t *source)
|
||||
: mVideoBuffer{},
|
||||
mAudioBuffer{},
|
||||
@@ -283,7 +307,6 @@ void AJASource::CaptureThread(AJAThread *thread, void *data)
|
||||
// etc...
|
||||
ULWord currentCardFrame = GetIndexForNTV2Channel(framestore) * 2;
|
||||
card->WaitForInputFieldID(NTV2_FIELD0, channel);
|
||||
|
||||
currentCardFrame ^= 1;
|
||||
|
||||
card->SetInputFrame(framestore, currentCardFrame);
|
||||
@@ -293,6 +316,11 @@ void AJASource::CaptureThread(AJAThread *thread, void *data)
|
||||
|
||||
obs_data_t *settings = obs_source_get_settings(ajaSource->mSource);
|
||||
|
||||
AudioRepacker *audioRepacker = new AudioRepacker(
|
||||
ConvertRepackFormat(sourceProps.SpeakerLayout(),
|
||||
sourceProps.swapFrontCenterLFE),
|
||||
sourceProps.audioSampleSize * 8);
|
||||
|
||||
while (ajaSource->IsCapturing()) {
|
||||
if (card->GetModelName() == "(Not Found)") {
|
||||
os_sleep_ms(250);
|
||||
@@ -340,15 +368,35 @@ void AJASource::CaptureThread(AJAThread *thread, void *data)
|
||||
ResetAudioBufferOffsets(card, audioSystem, offsets);
|
||||
} else {
|
||||
offsets.lastAddress = offsets.currentAddress;
|
||||
uint32_t sampleCount = offsets.bytesRead /
|
||||
(kDefaultAudioChannels *
|
||||
sourceProps.audioSampleSize);
|
||||
obs_source_audio audioPacket;
|
||||
audioPacket.samples_per_sec = 48000;
|
||||
audioPacket.format = AUDIO_FORMAT_32BIT;
|
||||
audioPacket.speakers = SPEAKERS_7POINT1;
|
||||
audioPacket.frames = offsets.bytesRead / 32;
|
||||
audioPacket.samples_per_sec =
|
||||
sourceProps.audioSampleRate;
|
||||
audioPacket.format = sourceProps.AudioFormat();
|
||||
audioPacket.speakers = sourceProps.SpeakerLayout();
|
||||
audioPacket.frames = sampleCount;
|
||||
audioPacket.timestamp =
|
||||
get_sample_time(audioPacket.frames, 48000);
|
||||
audioPacket.data[0] = (uint8_t *)ajaSource->mAudioBuffer
|
||||
.GetHostPointer();
|
||||
get_sample_time(audioPacket.frames,
|
||||
sourceProps.audioSampleRate);
|
||||
uint8_t *hostAudioBuffer =
|
||||
(uint8_t *)ajaSource->mAudioBuffer
|
||||
.GetHostPointer();
|
||||
if (sourceProps.audioNumChannels >= 2 &&
|
||||
sourceProps.audioNumChannels <= 6) {
|
||||
/* Repack 8ch audio to fit specified OBS speaker layout */
|
||||
audioRepacker->repack(hostAudioBuffer,
|
||||
sampleCount);
|
||||
audioPacket.data[0] =
|
||||
(*audioRepacker)->packet_buffer;
|
||||
} else {
|
||||
/* Silence, or pass-through 8ch of audio */
|
||||
if (sourceProps.audioNumChannels == 0)
|
||||
memset(hostAudioBuffer, 0,
|
||||
offsets.bytesRead);
|
||||
audioPacket.data[0] = hostAudioBuffer;
|
||||
}
|
||||
obs_source_output_audio(ajaSource->mSource,
|
||||
&audioPacket);
|
||||
}
|
||||
@@ -395,7 +443,10 @@ void AJASource::CaptureThread(AJAThread *thread, void *data)
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "AJASource::Capturethread: Thread loop stopped");
|
||||
|
||||
if (audioRepacker) {
|
||||
delete audioRepacker;
|
||||
audioRepacker = nullptr;
|
||||
}
|
||||
ajaSource->GenerateTestPattern(sourceProps.videoFormat,
|
||||
sourceProps.pixelFormat,
|
||||
NTV2_TestPatt_Black);
|
||||
@@ -682,6 +733,8 @@ bool aja_source_device_changed(void *data, obs_properties_t *props,
|
||||
obs_properties_get(props, kUIPropSDITransport.id);
|
||||
obs_property_t *sdi_4k_list =
|
||||
obs_properties_get(props, kUIPropSDITransport4K.id);
|
||||
obs_property_t *channel_format_list =
|
||||
obs_properties_get(props, kUIPropChannelFormat.id);
|
||||
|
||||
obs_property_list_clear(vid_fmt_list);
|
||||
obs_property_list_add_int(vid_fmt_list, obs_module_text("Auto"),
|
||||
@@ -702,6 +755,22 @@ bool aja_source_device_changed(void *data, obs_properties_t *props,
|
||||
obs_property_list_clear(sdi_4k_list);
|
||||
populate_sdi_4k_transport_list(sdi_4k_list);
|
||||
|
||||
obs_property_list_clear(channel_format_list);
|
||||
obs_property_list_add_int(channel_format_list, TEXT_CHANNEL_FORMAT_NONE,
|
||||
0);
|
||||
obs_property_list_add_int(channel_format_list,
|
||||
TEXT_CHANNEL_FORMAT_2_0CH, 2);
|
||||
obs_property_list_add_int(channel_format_list,
|
||||
TEXT_CHANNEL_FORMAT_2_1CH, 3);
|
||||
obs_property_list_add_int(channel_format_list,
|
||||
TEXT_CHANNEL_FORMAT_4_0CH, 4);
|
||||
obs_property_list_add_int(channel_format_list,
|
||||
TEXT_CHANNEL_FORMAT_4_1CH, 5);
|
||||
obs_property_list_add_int(channel_format_list,
|
||||
TEXT_CHANNEL_FORMAT_5_1CH, 6);
|
||||
obs_property_list_add_int(channel_format_list,
|
||||
TEXT_CHANNEL_FORMAT_7_1CH, 8);
|
||||
|
||||
populate_io_selection_input_list(cardID, ajaSource->GetName(), deviceID,
|
||||
io_select_list);
|
||||
|
||||
@@ -904,8 +973,12 @@ static void aja_source_update(void *data, obs_data_t *settings)
|
||||
obs_data_get_int(settings, kUIPropSDITransport.id));
|
||||
auto sdi_t4k_select = static_cast<SDITransport4K>(
|
||||
obs_data_get_int(settings, kUIPropSDITransport4K.id));
|
||||
auto num_audio_channels =
|
||||
obs_data_get_int(settings, kUIPropChannelFormat.id);
|
||||
bool deactivateWhileNotShowing =
|
||||
obs_data_get_bool(settings, kUIPropDeactivateWhenNotShowing.id);
|
||||
bool swapFrontCenterLFE =
|
||||
obs_data_get_bool(settings, kUIPropChannelSwap_FC_LFE.id);
|
||||
const std::string &wantCardID =
|
||||
obs_data_get_string(settings, kUIPropDevice.id);
|
||||
|
||||
@@ -989,6 +1062,8 @@ static void aja_source_update(void *data, obs_data_t *settings)
|
||||
? SDITransport::Unknown
|
||||
: static_cast<SDITransport>(sdi_trx_select);
|
||||
want_props.sdi4kTransport = sdi_t4k_select;
|
||||
want_props.audioNumChannels = (uint32_t)num_audio_channels;
|
||||
want_props.swapFrontCenterLFE = swapFrontCenterLFE;
|
||||
want_props.vpids.clear();
|
||||
want_props.deactivateWhileNotShowing = deactivateWhileNotShowing;
|
||||
if (aja::IsIOSelectionSDI(io_select)) {
|
||||
@@ -1099,11 +1174,21 @@ static obs_properties_t *aja_source_get_properties(void *data)
|
||||
obs_properties_add_list(props, kUIPropSDITransport4K.id,
|
||||
obs_module_text(kUIPropSDITransport4K.text),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_properties_add_list(props, kUIPropChannelFormat.id,
|
||||
obs_module_text(kUIPropChannelFormat.text),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_t *swap = obs_properties_add_bool(
|
||||
props, kUIPropChannelSwap_FC_LFE.id,
|
||||
obs_module_text(kUIPropChannelSwap_FC_LFE.text));
|
||||
obs_property_set_long_description(swap,
|
||||
kUIPropChannelSwap_FC_LFE.tooltip);
|
||||
obs_properties_add_bool(
|
||||
props, kUIPropDeactivateWhenNotShowing.id,
|
||||
obs_module_text(kUIPropDeactivateWhenNotShowing.text));
|
||||
obs_properties_add_bool(props, kUIPropBuffering.id,
|
||||
obs_module_text(kUIPropBuffering.text));
|
||||
obs_properties_add_bool(props, kUIPropBuffering.id,
|
||||
obs_module_text(kUIPropBuffering.text));
|
||||
|
||||
obs_property_set_modified_callback(vid_fmt_list,
|
||||
aja_video_format_changed);
|
||||
@@ -1128,6 +1213,10 @@ void aja_source_get_defaults(obs_data_t *settings)
|
||||
obs_data_set_default_int(
|
||||
settings, kUIPropSDITransport4K.id,
|
||||
static_cast<long long>(SDITransport4K::TwoSampleInterleave));
|
||||
obs_data_set_default_int(settings, kUIPropChannelFormat.id,
|
||||
kDefaultAudioCaptureChannels);
|
||||
obs_data_set_default_bool(settings, kUIPropChannelSwap_FC_LFE.id,
|
||||
false);
|
||||
obs_data_set_default_bool(settings, kUIPropDeactivateWhenNotShowing.id,
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "aja-props.hpp"
|
||||
#include "audio-repack.hpp"
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
|
||||
@@ -107,4 +107,24 @@ static const UIProperty kUIPropMultiViewAudioSource = {
|
||||
"ui_prop_multi_view_audio_source",
|
||||
"Multi View Audio Source",
|
||||
"",
|
||||
};
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropChannelFormat = {
|
||||
"ui_prop_channel_format",
|
||||
"ChannelFormat",
|
||||
"",
|
||||
};
|
||||
|
||||
static const UIProperty kUIPropChannelSwap_FC_LFE = {
|
||||
"ui_prop_channel_swap_fc_lfe",
|
||||
"SwapFC-LFE",
|
||||
"SwapFC-LFE.Tooltip",
|
||||
};
|
||||
|
||||
#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None")
|
||||
#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch")
|
||||
#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch")
|
||||
#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch")
|
||||
#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch")
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
#include "audio-repack.h"
|
||||
|
||||
#define USE_SIMD
|
||||
#ifdef USE_SIMD
|
||||
#include <util/sse-intrin.h>
|
||||
#endif
|
||||
|
||||
#define NUM_CHANNELS 8 /* max until OBS supports higher channel counts */
|
||||
|
||||
int check_buffer(struct audio_repack *repack, uint32_t frame_count)
|
||||
{
|
||||
const uint32_t new_size =
|
||||
frame_count * repack->base_dst_size + repack->pad_dst_size;
|
||||
|
||||
if (repack->packet_size < new_size) {
|
||||
repack->packet_buffer =
|
||||
brealloc(repack->packet_buffer, new_size);
|
||||
if (!repack->packet_buffer)
|
||||
return -1;
|
||||
repack->packet_size = new_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef USE_SIMD
|
||||
/*
|
||||
* Squash array of 8ch to new channel count
|
||||
* 16-bit PCM, SIMD version
|
||||
* For instance:
|
||||
* 2.1:
|
||||
*
|
||||
* | FL | FR | LFE | emp | emp | emp |emp |emp |
|
||||
* | | |
|
||||
* | FL | FR | LFE |
|
||||
*/
|
||||
int repack_squash16(struct audio_repack *repack, const uint8_t *bsrc,
|
||||
uint32_t frame_count)
|
||||
{
|
||||
if (check_buffer(repack, frame_count) < 0)
|
||||
return -1;
|
||||
|
||||
int squash = repack->squash_count;
|
||||
const __m128i *src = (__m128i *)bsrc;
|
||||
const __m128i *end = src + frame_count;
|
||||
uint16_t *dst = (uint16_t *)repack->packet_buffer;
|
||||
|
||||
/* Audio needs squashing in order to avoid resampling issues.
|
||||
* The condition checks for 7.1 audio for which no squash is needed.
|
||||
*/
|
||||
if (squash > 0) {
|
||||
while (src != end) {
|
||||
__m128i target = _mm_load_si128(src++);
|
||||
_mm_storeu_si128((__m128i *)dst, target);
|
||||
dst += NUM_CHANNELS - squash;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Squash array of 8ch and swap Front Center channel with LFE
|
||||
* 16-bit PCM, SIMD version
|
||||
* For instance:
|
||||
* 2.1:
|
||||
*
|
||||
* | FL | FR | FC | LFE | RL | RR | LC | RC |
|
||||
* | | |
|
||||
* | FL | FR | LFE | FC | RL | RR | LC | RC |
|
||||
*/
|
||||
int repack_squash_swap16(struct audio_repack *repack, const uint8_t *bsrc,
|
||||
uint32_t frame_count)
|
||||
{
|
||||
if (check_buffer(repack, frame_count) < 0)
|
||||
return -1;
|
||||
int squash = repack->squash_count;
|
||||
const __m128i *src = (__m128i *)bsrc;
|
||||
const __m128i *end = src + frame_count;
|
||||
uint16_t *dst = (uint16_t *)repack->packet_buffer;
|
||||
while (src != end) {
|
||||
__m128i target = _mm_load_si128(src++);
|
||||
__m128i buf =
|
||||
_mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0));
|
||||
_mm_storeu_si128((__m128i *)dst, buf);
|
||||
dst += NUM_CHANNELS - squash;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Squash array of 8ch to new channel count
|
||||
* 32-bit PCM, SIMD version
|
||||
*/
|
||||
int repack_squash32(struct audio_repack *repack, const uint8_t *bsrc,
|
||||
uint32_t frame_count)
|
||||
{
|
||||
if (check_buffer(repack, frame_count) < 0)
|
||||
return -1;
|
||||
|
||||
int squash = repack->squash_count;
|
||||
const __m128i *src = (__m128i *)bsrc;
|
||||
const __m128i *end =
|
||||
(__m128i *)(bsrc + (frame_count * repack->base_src_size));
|
||||
uint32_t *dst = (uint32_t *)repack->packet_buffer;
|
||||
|
||||
if (squash > 0) {
|
||||
while (src != end) {
|
||||
__m128i tgt_lo = _mm_loadu_si128(src++);
|
||||
__m128i tgt_hi = _mm_loadu_si128(src++);
|
||||
_mm_storeu_si128((__m128i *)dst, tgt_lo);
|
||||
dst += 4;
|
||||
_mm_storeu_si128((__m128i *)dst, tgt_hi);
|
||||
dst += 4;
|
||||
dst -= squash;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Squash array of 8ch to new channel count and swap FC with LFE
|
||||
* 32-bit PCM, SIMD version
|
||||
*/
|
||||
int repack_squash_swap32(struct audio_repack *repack, const uint8_t *bsrc,
|
||||
uint32_t frame_count)
|
||||
{
|
||||
if (check_buffer(repack, frame_count) < 0)
|
||||
return -1;
|
||||
int squash = repack->squash_count;
|
||||
const __m128i *src = (__m128i *)bsrc;
|
||||
const __m128i *end =
|
||||
(__m128i *)(bsrc + (frame_count * repack->base_src_size));
|
||||
uint32_t *dst = (uint32_t *)repack->packet_buffer;
|
||||
while (src != end) {
|
||||
__m128i tgt_lo = _mm_shuffle_epi32(_mm_loadu_si128(src++),
|
||||
_MM_SHUFFLE(2, 3, 1, 0));
|
||||
__m128i tgt_hi = _mm_loadu_si128(src++);
|
||||
_mm_storeu_si128((__m128i *)dst, tgt_lo);
|
||||
dst += 4;
|
||||
_mm_storeu_si128((__m128i *)dst, tgt_hi);
|
||||
dst += 4;
|
||||
dst -= squash;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Squash array of 8ch to new channel count
|
||||
* 16-bit or 32-bit PCM, C version
|
||||
*/
|
||||
int repack_squash(struct audio_repack *repack, const uint8_t *bsrc,
|
||||
uint32_t frame_count)
|
||||
{
|
||||
if (check_buffer(repack, frame_count) < 0)
|
||||
return -1;
|
||||
int squash = repack->squash_count;
|
||||
const uint8_t *src = bsrc;
|
||||
const uint8_t *end = src + frame_count * repack->base_src_size;
|
||||
uint8_t *dst = repack->packet_buffer;
|
||||
uint32_t new_channel_count = NUM_CHANNELS - squash;
|
||||
if (squash > 0) {
|
||||
while (src != end) {
|
||||
memcpy(dst, src,
|
||||
repack->bytes_per_sample * new_channel_count);
|
||||
dst += (new_channel_count * repack->bytes_per_sample);
|
||||
src += NUM_CHANNELS * repack->bytes_per_sample;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shuffle_8ch(uint8_t *dst, const uint8_t *src, size_t szb, int ch1, int ch2,
|
||||
int ch3, int ch4, int ch5, int ch6, int ch7, int ch8)
|
||||
{
|
||||
/* shuffle 8 channels of audio */
|
||||
for (size_t i = 0; i < szb; i++) {
|
||||
dst[0 * szb + i] = src[ch1 * szb + i];
|
||||
dst[1 * szb + i] = src[ch2 * szb + i];
|
||||
dst[2 * szb + i] = src[ch3 * szb + i];
|
||||
dst[3 * szb + i] = src[ch4 * szb + i];
|
||||
dst[4 * szb + i] = src[ch5 * szb + i];
|
||||
dst[5 * szb + i] = src[ch6 * szb + i];
|
||||
dst[6 * szb + i] = src[ch7 * szb + i];
|
||||
dst[7 * szb + i] = src[ch8 * szb + i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Squash array of 8ch to new channel count and swap FC with LFE
|
||||
* 16-bit or 32-bit PCM, C version
|
||||
*/
|
||||
int repack_squash_swap(struct audio_repack *repack, const uint8_t *bsrc,
|
||||
uint32_t frame_count)
|
||||
{
|
||||
if (check_buffer(repack, frame_count) < 0)
|
||||
return -1;
|
||||
|
||||
int squash = repack->squash_count;
|
||||
const uint8_t *src = bsrc;
|
||||
const uint8_t *end = src + (frame_count * repack->base_src_size);
|
||||
uint8_t *dst = repack->packet_buffer;
|
||||
uint32_t new_channel_count = NUM_CHANNELS - squash;
|
||||
if (squash > 0) {
|
||||
while (src != end) {
|
||||
shuffle_8ch(dst, src, 4, 0, 1, 3, 2, 4, 5, 6, 7);
|
||||
dst += (new_channel_count * repack->bytes_per_sample);
|
||||
src += (NUM_CHANNELS * repack->bytes_per_sample);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int audio_repack_init(struct audio_repack *repack,
|
||||
audio_repack_mode_t repack_mode, uint8_t bits_per_sample)
|
||||
{
|
||||
memset(repack, 0, sizeof(*repack));
|
||||
|
||||
if (bits_per_sample != 16 && bits_per_sample != 32)
|
||||
return -1;
|
||||
int _audio_repack_ch[9] = {2, 3, 4, 5, 6, 5, 6, 8, 8};
|
||||
int bytes_per_sample = (bits_per_sample / 8);
|
||||
repack->bytes_per_sample = bytes_per_sample;
|
||||
repack->base_src_size = NUM_CHANNELS * bytes_per_sample;
|
||||
repack->base_dst_size =
|
||||
_audio_repack_ch[repack_mode] * bytes_per_sample;
|
||||
uint32_t squash_count = NUM_CHANNELS - _audio_repack_ch[repack_mode];
|
||||
repack->pad_dst_size = squash_count * bytes_per_sample;
|
||||
repack->squash_count = squash_count;
|
||||
|
||||
#ifdef USE_SIMD
|
||||
if (bits_per_sample == 16) {
|
||||
repack->repack_func = &repack_squash16;
|
||||
if (repack_mode == repack_mode_8to5ch_swap ||
|
||||
repack_mode == repack_mode_8to6ch_swap ||
|
||||
repack_mode == repack_mode_8ch_swap)
|
||||
repack->repack_func = &repack_squash_swap16;
|
||||
} else if (bits_per_sample == 32) {
|
||||
repack->repack_func = &repack_squash32;
|
||||
if (repack_mode == repack_mode_8to5ch_swap ||
|
||||
repack_mode == repack_mode_8to6ch_swap ||
|
||||
repack_mode == repack_mode_8ch_swap)
|
||||
repack->repack_func = &repack_squash_swap32;
|
||||
}
|
||||
#else
|
||||
repack->repack_func = &repack_squash;
|
||||
if (repack_mode == repack_mode_8to5ch_swap ||
|
||||
repack_mode == repack_mode_8to6ch_swap ||
|
||||
repack_mode == repack_mode_8ch_swap)
|
||||
repack->repack_func = &repack_squash_swap;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void audio_repack_free(struct audio_repack *repack)
|
||||
{
|
||||
if (repack->packet_buffer)
|
||||
bfree(repack->packet_buffer);
|
||||
|
||||
memset(repack, 0, sizeof(*repack));
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <obs.h>
|
||||
|
||||
struct audio_repack;
|
||||
|
||||
typedef int (*audio_repack_func_t)(struct audio_repack *, const uint8_t *,
|
||||
uint32_t);
|
||||
|
||||
struct audio_repack {
|
||||
uint8_t *packet_buffer;
|
||||
uint32_t packet_size;
|
||||
|
||||
uint32_t base_src_size;
|
||||
uint32_t base_dst_size;
|
||||
uint32_t pad_dst_size;
|
||||
uint32_t squash_count;
|
||||
uint32_t bytes_per_sample;
|
||||
|
||||
audio_repack_func_t repack_func;
|
||||
};
|
||||
|
||||
enum _audio_repack_mode {
|
||||
repack_mode_8to2ch = 0,
|
||||
repack_mode_8to3ch,
|
||||
repack_mode_8to4ch,
|
||||
repack_mode_8to5ch,
|
||||
repack_mode_8to6ch,
|
||||
repack_mode_8to5ch_swap,
|
||||
repack_mode_8to6ch_swap,
|
||||
repack_mode_8ch_swap,
|
||||
repack_mode_8ch,
|
||||
};
|
||||
|
||||
typedef enum _audio_repack_mode audio_repack_mode_t;
|
||||
|
||||
extern int audio_repack_init(struct audio_repack *repack,
|
||||
audio_repack_mode_t repack_mode,
|
||||
uint8_t bits_per_sample);
|
||||
extern void audio_repack_free(struct audio_repack *repack);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "audio-repack.h"
|
||||
|
||||
class AudioRepacker {
|
||||
struct audio_repack arepack;
|
||||
|
||||
public:
|
||||
inline AudioRepacker(audio_repack_mode_t repack_mode,
|
||||
int bits_per_sample)
|
||||
{
|
||||
audio_repack_init(&arepack, repack_mode, bits_per_sample);
|
||||
}
|
||||
inline ~AudioRepacker() { audio_repack_free(&arepack); }
|
||||
|
||||
inline int repack(const uint8_t *src, uint32_t frame_size)
|
||||
{
|
||||
return (*arepack.repack_func)(&arepack, src, frame_size);
|
||||
}
|
||||
|
||||
inline operator struct audio_repack *() { return &arepack; }
|
||||
inline struct audio_repack *operator->() { return &arepack; }
|
||||
};
|
||||
@@ -15,3 +15,13 @@ IOSelect="Select..."
|
||||
SDITransport="SDI Transport"
|
||||
SDITransport4K="SDI 4K Transport"
|
||||
Auto="Auto"
|
||||
ChannelFormat="Channel"
|
||||
ChannelFormat.None="None"
|
||||
ChannelFormat.2_0ch="2ch"
|
||||
ChannelFormat.2_1ch="2.1ch"
|
||||
ChannelFormat.4_0ch="4ch"
|
||||
ChannelFormat.4_1ch="4.1ch"
|
||||
ChannelFormat.5_1ch="5.1ch"
|
||||
ChannelFormat.7_1ch="7.1ch"
|
||||
SwapFC-LFE="Swap FC and LFE"
|
||||
SwapFC-LFE.Tooltip="Swap Front Center Channel and LFE Channel"
|
||||
|
||||
Reference in New Issue
Block a user