Implement a few more audio options/functions

Implement a few audio options in to the user interface as well as a few
inline audio functions in audio-io.h.

Make it so ffmpeg plugin automatically converts to the desired format.

Use regular interleaved float internally for audio instead of planar
float.
This commit is contained in:
jp9000 2014-02-23 16:27:19 -07:00
parent 0ff0d32731
commit c232ebde15
10 changed files with 311 additions and 54 deletions

View File

@ -519,6 +519,14 @@ bool audio_output_connect(audio_t audio,
audio->info.samples_per_sec;
}
if (input.conversion.format == AUDIO_FORMAT_UNKNOWN)
input.conversion.format = audio->info.format;
if (input.conversion.speakers == SPEAKERS_UNKNOWN)
input.conversion.speakers = audio->info.speakers;
if (input.conversion.samples_per_sec == 0)
input.conversion.samples_per_sec =
audio->info.samples_per_sec;
success = audio_input_init(&input, audio);
if (success)
da_push_back(audio->inputs, &input);

View File

@ -127,7 +127,7 @@ static inline size_t get_audio_bytes_per_channel(enum audio_format type)
return 0;
}
static inline size_t is_audio_planar(enum audio_format type)
static inline bool is_audio_planar(enum audio_format type)
{
switch (type) {
case AUDIO_FORMAT_U8BIT:
@ -149,10 +149,18 @@ static inline size_t is_audio_planar(enum audio_format type)
return false;
}
static inline size_t get_audio_planes(enum audio_format type,
enum speaker_layout speakers)
{
return (is_audio_planar(type) ? get_audio_channels(speakers) : 1);
}
static inline size_t get_audio_size(enum audio_format type,
enum speaker_layout speakers, uint32_t frames)
{
return get_audio_channels(speakers) *
bool planar = is_audio_planar(type);
return (planar ? 1 : get_audio_channels(speakers)) *
get_audio_bytes_per_channel(type) *
frames;
}

View File

@ -2,6 +2,9 @@
<ui version="4.0">
<class>NameDialog</class>
<widget class="QDialog" name="NameDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
@ -10,12 +13,6 @@
<height>102</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Dialog</string>
</property>
@ -31,6 +28,9 @@
<property name="text">
<string notr="true">TextLabel</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>

View File

@ -77,7 +77,7 @@
<item>
<widget class="QStackedWidget" name="settingsPages">
<property name="currentIndex">
<number>3</number>
<number>2</number>
</property>
<widget class="QWidget" name="generalPage">
<layout class="QFormLayout" name="formLayout_2">
@ -127,14 +127,66 @@
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="1">
<widget class="QComboBox" name="desktopAudioDevice1">
<property name="enabled">
<bool>false</bool>
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Sample Rate:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<item row="0" column="1">
<widget class="QComboBox" name="sampleRate">
<property name="currentText">
<string notr="true">44.1khz</string>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>22.05khz</string>
</property>
</item>
<item>
<property name="text">
<string>44.1khz</string>
</property>
</item>
<item>
<property name="text">
<string>48khz</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Channels:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="channelSetup">
<property name="currentText">
<string>Stereo</string>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Mono</string>
</property>
</item>
<item>
<property name="text">
<string>Stereo</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
@ -150,7 +202,14 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="1">
<widget class="QComboBox" name="desktopAudioDevice1">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Desktop Audio Device 2:</string>
@ -160,55 +219,75 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="3" column="1">
<widget class="QComboBox" name="desktopAudioDevice2">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Mic/Aux Audio Device 1:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="4" column="1">
<widget class="QComboBox" name="auxAudioDevice1">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Mic/Aux Audio Device 2:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="5" column="1">
<widget class="QComboBox" name="auxAudioDevice2">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Mic/Aux Audio Device 3:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="6" column="1">
<widget class="QComboBox" name="auxAudioDevice3">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="audioBufferingTime">
<property name="minimum">
<number>60</number>
</property>
<property name="maximum">
<number>20000</number>
</property>
<property name="value">
<number>700</number>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Audio Buffering Time (milliseconds):</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="videoPage">

View File

@ -96,6 +96,11 @@ bool OBSApp::InitGlobalConfigDefaults()
config_set_default_uint(globalConfig, "Video", "FPSDen", 1);
config_set_default_uint(globalConfig, "Video", "FPSNS", 33333333);
config_set_default_uint(globalConfig, "Audio", "SampleRate", 44100);
config_set_default_string(globalConfig, "Audio", "ChannelSetup",
"Stereo");
config_set_default_uint(globalConfig, "Audio", "BufferingTime", 700);
return true;
}

View File

@ -313,13 +313,23 @@ bool OBSBasic::ResetVideo()
bool OBSBasic::ResetAudio()
{
/* TODO: load audio settings from config */
struct audio_output_info ai;
ai.name = "test";
ai.samples_per_sec = 44100;
ai.format = AUDIO_FORMAT_FLOAT_PLANAR;
ai.speakers = SPEAKERS_STEREO;
ai.buffer_ms = 700;
ai.name = "Main Audio Track";
ai.format = AUDIO_FORMAT_FLOAT;
ai.samples_per_sec = config_get_uint(GetGlobalConfig(), "Audio",
"SampleRate");
const char *channelSetupStr = config_get_string(GetGlobalConfig(),
"Audio", "ChannelSetup");
if (strcmp(channelSetupStr, "Mono") == 0)
ai.speakers = SPEAKERS_MONO;
else
ai.speakers = SPEAKERS_STEREO;
ai.buffer_ms = config_get_uint(GetGlobalConfig(), "Audio",
"BufferingTime");
return obs_reset_audio(&ai);
}

View File

@ -178,6 +178,8 @@ static const size_t numVals = sizeof(vals)/sizeof(double);
void OBSBasicSettings::ResetDownscales(uint32_t cx, uint32_t cy)
{
ui->outputResolution->clear();
for (size_t idx = 0; idx < numVals; idx++) {
uint32_t downscaleCX = uint32_t(double(cx) / vals[idx]);
uint32_t downscaleCY = uint32_t(double(cy) / vals[idx]);
@ -196,7 +198,6 @@ void OBSBasicSettings::LoadResolutionLists()
vector<MonitorInfo> monitors;
ui->baseResolution->clear();
ui->outputResolution->clear();
GetMonitors(monitors);
@ -270,14 +271,47 @@ void OBSBasicSettings::LoadVideoSettings()
loading = false;
}
void OBSBasicSettings::LoadAudioSettings()
{
uint32_t sampleRate = config_get_uint(GetGlobalConfig(), "Audio",
"SampleRate");
const char *speakers = config_get_string(GetGlobalConfig(), "Audio",
"ChannelSetup");
uint32_t bufferingTime = config_get_uint(GetGlobalConfig(), "Audio",
"BufferingTime");
loading = true;
const char *str;
if (sampleRate == 22050)
str = "22.05khz";
else if (sampleRate == 48000)
str = "48khz";
else
str = "44.1khz";
int sampleRateIdx = ui->sampleRate->findText(str);
if (sampleRateIdx != -1)
ui->sampleRate->setCurrentIndex(sampleRateIdx);
if (strcmp(speakers, "Mono") == 0)
ui->channelSetup->setCurrentIndex(0);
else
ui->channelSetup->setCurrentIndex(1);
ui->audioBufferingTime->setValue(bufferingTime);
loading = false;
}
void OBSBasicSettings::LoadSettings(bool changedOnly)
{
if (!changedOnly || generalChanged)
LoadGeneralSettings();
//if (!changedOnly || outputChanged)
// LoadOutputSettings();
//if (!changedOnly || audioChanged)
// LoadOutputSettings();
if (!changedOnly || audioChanged)
LoadAudioSettings();
if (!changedOnly || videoChanged)
LoadVideoSettings();
}
@ -331,14 +365,39 @@ void OBSBasicSettings::SaveVideoSettings()
window->ResetVideo();
}
void OBSBasicSettings::SaveAudioSettings()
{
QString sampleRateStr = ui->sampleRate->currentText();
int channelSetupIdx = ui->channelSetup->currentIndex();
int bufferingTime = ui->audioBufferingTime->value();
const char *channelSetup;
if (channelSetupIdx == 0)
channelSetup = "Mono";
else
channelSetup = "Stereo";
int sampleRate = 44100;
if (sampleRateStr == "22.05khz")
sampleRate = 22050;
else if (sampleRateStr == "48khz")
sampleRate = 48000;
config_set_uint(GetGlobalConfig(), "Audio", "SampleRate", sampleRate);
config_set_string(GetGlobalConfig(), "Audio", "ChannelSetup",
channelSetup);
config_set_uint(GetGlobalConfig(), "Audio", "BufferingTime",
bufferingTime);
}
void OBSBasicSettings::SaveSettings()
{
if (generalChanged)
SaveGeneralSettings();
//if (outputChanged)
// SaveOutputSettings();
//if (audioChanged)
// SaveAudioSettings();
if (audioChanged)
SaveAudioSettings();
if (videoChanged)
SaveVideoSettings();
@ -430,6 +489,30 @@ void OBSBasicSettings::on_language_currentIndexChanged(int index)
UNUSED_PARAMETER(index);
}
void OBSBasicSettings::on_sampleRate_currentIndexChanged(int index)
{
if (!loading)
audioChanged = true;
UNUSED_PARAMETER(index);
}
void OBSBasicSettings::on_channelSetup_currentIndexChanged(int index)
{
if (!loading)
audioChanged = true;
UNUSED_PARAMETER(index);
}
void OBSBasicSettings::on_audioBufferingTime_valueChanged(int value)
{
if (!loading)
audioChanged = true;
UNUSED_PARAMETER(value);
}
void OBSBasicSettings::on_renderer_currentIndexChanged(int index)
{
if (!loading) {
@ -450,8 +533,14 @@ void OBSBasicSettings::on_fpsType_currentIndexChanged(int index)
void OBSBasicSettings::on_baseResolution_editTextChanged(const QString &text)
{
if (!loading && ValidResolutions(ui.get()))
if (!loading && ValidResolutions(ui.get())) {
QString baseResolution = ui->baseResolution->currentText();
uint32_t cx, cy;
ConvertResText(QT_TO_UTF8(baseResolution), cx, cy);
ResetDownscales(cx, cy);
videoChanged = true;
}
UNUSED_PARAMETER(text);
}
@ -463,3 +552,35 @@ void OBSBasicSettings::on_outputResolution_editTextChanged(const QString &text)
UNUSED_PARAMETER(text);
}
void OBSBasicSettings::on_fpsCommon_currentIndexChanged(int index)
{
if (!loading)
videoChanged = true;
UNUSED_PARAMETER(index);
}
void OBSBasicSettings::on_fpsInteger_valueChanged(int value)
{
if (!loading)
videoChanged = true;
UNUSED_PARAMETER(value);
}
void OBSBasicSettings::on_fpsNumerator_valueChanged(int value)
{
if (!loading)
videoChanged = true;
UNUSED_PARAMETER(value);
}
void OBSBasicSettings::on_fpsDenominator_valueChanged(int value)
{
if (!loading)
videoChanged = true;
UNUSED_PARAMETER(value);
}

View File

@ -56,8 +56,9 @@ private:
void LoadGeneralSettings();
//void LoadOutputSettings();
//void LoadAudioSettings();
void LoadAudioSettings();
void LoadVideoSettings();
void LoadSettings(bool changedOnly);
/* general */
void LoadLanguageList();
@ -67,11 +68,10 @@ private:
void ResetDownscales(uint32_t cx, uint32_t cy);
void LoadResolutionLists();
void LoadFPSData();
void LoadSettings(bool changedOnly);
void SaveGeneralSettings();
//void SaveOutputSettings();
//void SaveAudioSettings();
void SaveAudioSettings();
void SaveVideoSettings();
void SaveSettings();
@ -81,10 +81,18 @@ private slots:
void on_language_currentIndexChanged(int index);
void on_sampleRate_currentIndexChanged(int index);
void on_channelSetup_currentIndexChanged(int index);
void on_audioBufferingTime_valueChanged(int index);
void on_renderer_currentIndexChanged(int index);
void on_fpsType_currentIndexChanged(int index);
void on_baseResolution_editTextChanged(const QString &text);
void on_outputResolution_editTextChanged(const QString &text);
void on_fpsCommon_currentIndexChanged(int index);
void on_fpsInteger_valueChanged(int value);
void on_fpsNumerator_valueChanged(int value);
void on_fpsDenominator_valueChanged(int value);
protected:
virtual void closeEvent(QCloseEvent *event);

View File

@ -21,6 +21,9 @@
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
/* NOTE: much of this stuff is test stuff that was more or less copied from
* the muxing.c ffmpeg example */
struct ffmpeg_data {
AVStream *video;
AVStream *audio;
@ -34,6 +37,9 @@ struct ffmpeg_data {
int frame_size;
int total_frames;
enum audio_format audio_format;
size_t audio_planes;
size_t audio_size;
struct circlebuf excess_frames[MAX_AV_PLANES];
uint8_t *samples[MAX_AV_PLANES];
AVFrame *aframe;
@ -52,12 +58,6 @@ struct ffmpeg_output {
/* ------------------------------------------------------------------------- */
/* TODO: remove these later */
#define SPS_TODO 44100
/* NOTE: much of this stuff is test stuff that was more or less copied from
* the muxing.c ffmpeg example */
static inline enum AVPixelFormat obs_to_ffmpeg_video_format(
enum video_format format)
{
@ -76,6 +76,24 @@ static inline enum AVPixelFormat obs_to_ffmpeg_video_format(
return AV_PIX_FMT_NONE;
}
static inline enum audio_format convert_ffmpeg_sample_format(
enum AVSampleFormat format)
{
switch ((uint32_t)format) {
case AV_SAMPLE_FMT_U8: return AUDIO_FORMAT_U8BIT;
case AV_SAMPLE_FMT_S16: return AUDIO_FORMAT_16BIT;
case AV_SAMPLE_FMT_S32: return AUDIO_FORMAT_32BIT;
case AV_SAMPLE_FMT_FLT: return AUDIO_FORMAT_FLOAT;
case AV_SAMPLE_FMT_U8P: return AUDIO_FORMAT_U8BIT_PLANAR;
case AV_SAMPLE_FMT_S16P: return AUDIO_FORMAT_16BIT_PLANAR;
case AV_SAMPLE_FMT_S32P: return AUDIO_FORMAT_32BIT_PLANAR;
case AV_SAMPLE_FMT_FLTP: return AUDIO_FORMAT_FLOAT_PLANAR;
}
/* shouldn't get here */
return AUDIO_FORMAT_16BIT;
}
static bool new_stream(struct ffmpeg_data *data, AVStream **stream,
AVCodec **codec, enum AVCodecID id)
{
@ -237,6 +255,10 @@ static bool create_audio_stream(struct ffmpeg_data *data)
context->sample_fmt = data->acodec->sample_fmts ?
data->acodec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
data->audio_format = convert_ffmpeg_sample_format(context->sample_fmt);
data->audio_planes = get_audio_planes(data->audio_format, aoi.speakers);
data->audio_size = get_audio_size(data->audio_format, aoi.speakers, 1);
if (data->output->oformat->flags & AVFMT_GLOBALHEADER)
context->flags |= CODEC_FLAG_GLOBAL_HEADER;
@ -532,21 +554,19 @@ static void receive_audio(void *param, const struct audio_data *frame)
struct ffmpeg_data *data = &output->ff_data;
AVCodecContext *context = data->audio->codec;
size_t planes = audio_output_planes(obs_audio());
size_t block_size = audio_output_blocksize(obs_audio());
size_t frame_size_bytes = (size_t)data->frame_size * block_size;
size_t frame_size_bytes = (size_t)data->frame_size * data->audio_size;
for (size_t i = 0; i < planes; i++)
for (size_t i = 0; i < data->audio_planes; i++)
circlebuf_push_back(&data->excess_frames[i], frame->data[0],
frame->frames * block_size);
frame->frames * data->audio_size);
while (data->excess_frames[0].size >= frame_size_bytes) {
for (size_t i = 0; i < planes; i++)
for (size_t i = 0; i < data->audio_planes; i++)
circlebuf_pop_front(&data->excess_frames[i],
data->samples[i], frame_size_bytes);
encode_audio(data, context, block_size);
encode_audio(data, context, data->audio_size);
}
}
@ -575,9 +595,7 @@ static bool ffmpeg_output_start(void *data)
return false;
struct audio_convert_info aci = {
.samples_per_sec = SPS_TODO,
.format = AUDIO_FORMAT_FLOAT_PLANAR,
.speakers = SPEAKERS_STEREO
.format = output->ff_data.audio_format
};
struct video_scale_info vsi = {

View File

@ -37,7 +37,7 @@ static void *sinewave_thread(void *pdata)
if (cos_val > M_PI_X2)
cos_val -= M_PI_X2;
double wave = cos(cos_val);
double wave = cos(cos_val) * 0.5;
bytes[i] = (uint8_t)((wave+1.0)*0.5 * 255.0);
}