blob: 14450943a0f98c3714eeb95007c0b62b3dfcfa82 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/task.h>
#include "garnet/bin/media/signal_generator/signal_generator.h"
#include "lib/component/cpp/startup_context.h"
#include "lib/fxl/command_line.h"
#include "lib/fxl/strings/string_number_conversions.h"
namespace {
constexpr char kNumChannelsSwitch[] = "chans";
constexpr char kNumChannelsDefault[] = "2";
constexpr char kInt16FormatSwitch[] = "int16";
constexpr char kInt24FormatSwitch[] = "int24";
constexpr char kFrameRateSwitch[] = "rate";
constexpr char kFrameRateDefaultHz[] = "48000";
constexpr char kSineWaveSwitch[] = "sine";
constexpr char kSquareWaveSwitch[] = "square";
constexpr char kSawtoothWaveSwitch[] = "saw";
constexpr char kWhiteNoiseSwitch[] = "noise";
constexpr char kFrequencyDefaultHz[] = "440.0";
constexpr char kDurationSwitch[] = "dur";
constexpr char kDurationDefaultSecs[] = "2.0";
constexpr char kAmplitudeSwitch[] = "amp";
constexpr char kAmplitudeDefaultScale[] = "0.25";
constexpr char kSaveToFileSwitch[] = "wav";
constexpr char kSaveToFileDefaultName[] = "/tmp/signal_generator.wav";
constexpr char kFramesPerPayloadSwitch[] = "frames";
constexpr char kFramesPerPayloadDefault[] = "480";
constexpr char kStreamGainSwitch[] = "gain";
constexpr char kStreamGainDefaultDb[] = "0.0";
constexpr char kStreamMuteSwitch[] = "mute";
constexpr char kStreamMuteDefault[] = "1";
constexpr char kStreamRampSwitch[] = "ramp";
constexpr char kStreamRampDurationSwitch[] = "rampdur";
constexpr char kStreamRampTargetGainSwitch[] = "endgain";
constexpr char kStreamRampTargetGainDefaultDb[] = "-75.0";
constexpr char kSystemGainSwitch[] = "sgain";
constexpr char kSystemGainDefaultDb[] = "-12.0";
constexpr char kSystemMuteSwitch[] = "smute";
constexpr char kSystemMuteDefault[] = "1";
constexpr char kPlayToLastSwitch[] = "last";
constexpr char kPlayToAllSwitch[] = "all";
constexpr char kHelpSwitch[] = "help";
} // namespace
void usage(const char* prog_name) {
printf("\nUsage: %s [--option] [...]\n", prog_name);
printf("Generate and play an audio signal to the preferred output device.\n");
printf("\nAdditional optional settings include:\n");
printf("\n\t By default, set stream format to %s-channel float32 at %s Hz\n",
kNumChannelsDefault, kFrameRateDefaultHz);
printf("\t--%s=<NUM_CHANS>\tSpecify number of channels\n",
kNumChannelsSwitch);
printf("\t--%s\t\t\tUse 16-bit integer samples\n", kInt16FormatSwitch);
printf(
"\t--%s\t\t\tUse 24-in-32-bit integer samples (left-justified "
"'padded-24')\n",
kInt24FormatSwitch);
printf("\t--%s=<FRAME_RATE>\tSet frame rate in Hz\n", kFrameRateSwitch);
printf("\n\t By default, signal is a %s Hz sine wave\n",
kFrequencyDefaultHz);
printf("\t--%s[=<FREQ>] \tPlay sine wave at given frequency (Hz)\n",
kSineWaveSwitch);
printf("\t--%s[=<FREQ>] \tPlay square wave at given frequency\n",
kSquareWaveSwitch);
printf("\t--%s[=<FREQ>] \tPlay rising sawtooth wave at given frequency\n",
kSawtoothWaveSwitch);
printf("\t--%s \t\tPlay pseudo-random 'white' noise\n", kWhiteNoiseSwitch);
printf("\t If no frequency is provided (e.g. '--%s'), %s Hz is used\n",
kSquareWaveSwitch, kFrequencyDefaultHz);
printf("\n\t By default, signal plays for %s seconds, at amplitude %s\n",
kDurationDefaultSecs, kAmplitudeDefaultScale);
printf("\t--%s=<DURATION_SEC>\tSet playback length in seconds\n",
kDurationSwitch);
printf("\t--%s=<AMPL>\t\tSet amplitude (full-scale=1.0, silence=0.0)\n",
kAmplitudeSwitch);
printf(
"\n\t--%s[=<FILEPATH>]\tSave to .wav file ('%s' if only '--%s' is "
"provided)\n",
kSaveToFileSwitch, kSaveToFileDefaultName, kSaveToFileSwitch);
printf(
"\t Subsequent settings (e.g. gain) do not affect .wav file contents\n");
printf(
"\n\t By default, submit data to the renderer using buffers of %s "
"frames\n",
kFramesPerPayloadDefault);
printf("\t--%s=<FRAMES>\tSet data buffer size in frames \n",
kFramesPerPayloadSwitch);
printf(
"\n\t By default, AudioRenderer gain and mute are not set (unity 0 dB "
"unmuted, no ramping)\n");
printf(
"\t--%s[=<GAIN_DB>]\tSet stream gain (dB in [%.1f, %.1f]; %s if only "
"'--%s' is provided)\n",
kStreamGainSwitch, fuchsia::media::MUTED_GAIN_DB,
fuchsia::media::MAX_GAIN_DB, kStreamGainDefaultDb, kStreamGainSwitch);
printf(
"\t--%s[=<0|1>]\t\tSet stream mute (0=Unmute or 1=Mute; Mute if only "
"'--%s' is provided)\n",
kStreamMuteSwitch, kStreamMuteSwitch);
printf(
"\t--%s\t\t\tSmoothly ramp gain from initial value to a target %s dB "
"by end-of-signal\n",
kStreamRampSwitch, kStreamRampTargetGainDefaultDb);
printf("\t\t\t\tIf '--%s' is not provided, ramping starts at unity gain\n",
kStreamGainSwitch);
printf(
"\t--%s=<GAIN_DB>\tSet a different ramp target gain (dB). Implies "
"'--%s'\n",
kStreamRampTargetGainSwitch, kStreamRampSwitch);
printf(
"\t--%s=<DURATION_MS>\tSet a specific ramp duration in milliseconds. "
"Implies '--%s'\n",
kStreamRampDurationSwitch, kStreamRampSwitch);
printf("\n\t By default, System Gain and Mute are unchanged\n");
printf(
"\t--%s[=<GAIN_DB>]\tSet System Gain (dB in [%.1f, 0.0]; %s if only "
"'--%s' is provided)\n",
kSystemGainSwitch, fuchsia::media::MUTED_GAIN_DB, kSystemGainDefaultDb,
kSystemGainSwitch);
printf(
"\t--%s[=<0|1>]\t\tSet System Mute (0=Unmute or 1=Mute; Mute if only "
"'--%s' is provided)\n",
kSystemMuteSwitch, kSystemMuteSwitch);
printf("\t Note: changes to System Gain/Mute persist after playback\n");
printf("\n\t By default, system audio output routing policy is unchanged\n");
printf("\t--%s\t\t\tSet 'Play to Most-Recently-Plugged' routing policy\n",
kPlayToLastSwitch);
printf("\t--%s\t\t\tSet 'Play to All' routing policy\n", kPlayToAllSwitch);
printf("\t\t\t\tNote: changes to routing policy persist after playback\n");
printf("\n\t--%s, --?\t\tShow this message\n\n", kHelpSwitch);
}
int main(int argc, const char** argv) {
const auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
if (command_line.HasOption("?") || command_line.HasOption(kHelpSwitch)) {
usage(argv[0]);
return 0;
}
async::Loop loop(&kAsyncLoopConfigAttachToThread);
auto startup_context = component::StartupContext::CreateFromStartupInfo();
media::tools::MediaApp media_app([&loop]() {
async::PostTask(loop.dispatcher(), [&loop]() { loop.Quit(); });
});
// Handle channels and frame-rate
std::string num_channels_str = command_line.GetOptionValueWithDefault(
kNumChannelsSwitch, kNumChannelsDefault);
media_app.set_num_channels(fxl::StringToNumber<uint32_t>(num_channels_str));
std::string frame_rate_str = command_line.GetOptionValueWithDefault(
kFrameRateSwitch, kFrameRateDefaultHz);
media_app.set_frame_rate(fxl::StringToNumber<uint32_t>(frame_rate_str));
// Handle signal format
if (command_line.HasOption(kInt16FormatSwitch)) {
// Don't allow the user to specify more than one container format
if (command_line.HasOption(kInt24FormatSwitch)) {
usage(argv[0]);
return 0;
}
media_app.set_int16_format(true);
}
if (command_line.HasOption(kInt24FormatSwitch)) {
media_app.set_int24_format(true);
}
// Handle signal type and frequency specifications.
// If >1 type is specified, obey usage order: sine, square, saw, noise.
std::string frequency_str = "";
if (command_line.HasOption(kSineWaveSwitch)) {
media_app.set_output_type(media::tools::kOutputTypeSine);
command_line.GetOptionValue(kSineWaveSwitch, &frequency_str);
} else if (command_line.HasOption(kSquareWaveSwitch)) {
media_app.set_output_type(media::tools::kOutputTypeSquare);
command_line.GetOptionValue(kSquareWaveSwitch, &frequency_str);
} else if (command_line.HasOption(kSawtoothWaveSwitch)) {
media_app.set_output_type(media::tools::kOutputTypeSawtooth);
command_line.GetOptionValue(kSawtoothWaveSwitch, &frequency_str);
} else if (command_line.HasOption(kWhiteNoiseSwitch)) {
media_app.set_output_type(media::tools::kOutputTypeNoise);
} else {
media_app.set_output_type(media::tools::kOutputTypeSine);
}
if (frequency_str == "") {
frequency_str = kFrequencyDefaultHz;
}
media_app.set_frequency(std::stod(frequency_str));
// Handle duration and amplitude of generated signal
std::string amplitude_str = command_line.GetOptionValueWithDefault(
kAmplitudeSwitch, kAmplitudeDefaultScale);
if (amplitude_str != "") {
media_app.set_amplitude(std::stof(amplitude_str));
}
std::string duration_str = command_line.GetOptionValueWithDefault(
kDurationSwitch, kDurationDefaultSecs);
if (duration_str != "") {
media_app.set_duration(std::stod(duration_str));
}
// Handle payload buffer size
std::string frames_per_payload_str = command_line.GetOptionValueWithDefault(
kFramesPerPayloadSwitch, kFramesPerPayloadDefault);
media_app.set_frames_per_payload(
fxl::StringToNumber<uint32_t>(frames_per_payload_str));
// Handle stream gain
if (command_line.HasOption(kStreamGainSwitch)) {
std::string stream_gain_str;
command_line.GetOptionValue(kStreamGainSwitch, &stream_gain_str);
if (stream_gain_str == "") {
stream_gain_str = kStreamGainDefaultDb;
}
media_app.set_stream_gain(std::stof(stream_gain_str));
}
if (command_line.HasOption(kStreamMuteSwitch)) {
std::string stream_mute_str;
command_line.GetOptionValue(kStreamMuteSwitch, &stream_mute_str);
if (stream_mute_str == "") {
stream_mute_str = kStreamMuteDefault;
}
media_app.set_stream_mute(fxl::StringToNumber<uint32_t>(stream_mute_str) !=
0);
}
// Handle stream gain ramping, target gain and ramp duration.
if (command_line.HasOption(kStreamRampSwitch) ||
command_line.HasOption(kStreamRampTargetGainSwitch) ||
command_line.HasOption(kStreamRampDurationSwitch)) {
media_app.set_will_ramp_stream_gain();
std::string target_gain_db_str = command_line.GetOptionValueWithDefault(
kStreamRampTargetGainSwitch, kStreamRampTargetGainDefaultDb);
if (target_gain_db_str == "") {
target_gain_db_str = kStreamRampTargetGainDefaultDb;
}
media_app.set_ramp_target_gain_db(std::stof(target_gain_db_str));
// Convert signal duration of doublefloat seconds, to int64 nanoseconds.
auto ramp_duration_nsec =
static_cast<zx_duration_t>(media_app.get_duration() * 1000000000.0);
if (command_line.HasOption(kStreamRampDurationSwitch)) {
std::string ramp_duration_str = "";
command_line.GetOptionValue(kStreamRampDurationSwitch,
&ramp_duration_str);
if (ramp_duration_str != "") {
// Convert input of doublefloat milliseconds, to int64 nanoseconds.
ramp_duration_nsec = static_cast<zx_duration_t>(
std::stod(ramp_duration_str) * 1000000.0);
}
}
media_app.set_ramp_duration_nsec(ramp_duration_nsec);
}
// Handle system gain and system mute
if (command_line.HasOption(kSystemGainSwitch)) {
std::string system_gain_str;
command_line.GetOptionValue(kSystemGainSwitch, &system_gain_str);
if (system_gain_str == "") {
system_gain_str = kSystemGainDefaultDb;
}
media_app.set_system_gain(std::stof(system_gain_str));
}
if (command_line.HasOption(kSystemMuteSwitch)) {
std::string system_mute_str;
command_line.GetOptionValue(kSystemMuteSwitch, &system_mute_str);
if (system_mute_str == "") {
system_mute_str = kSystemMuteDefault;
}
media_app.set_system_mute(fxl::StringToNumber<uint32_t>(system_mute_str) !=
0);
}
// Handle output routing policy
if (command_line.HasOption(kPlayToLastSwitch)) {
// Don't allow the user to specify both policies
if (command_line.HasOption(kPlayToAllSwitch)) {
usage(argv[0]);
return 0;
}
media_app.set_audio_policy(
fuchsia::media::AudioOutputRoutingPolicy::LAST_PLUGGED_OUTPUT);
}
if (command_line.HasOption(kPlayToAllSwitch)) {
media_app.set_audio_policy(
fuchsia::media::AudioOutputRoutingPolicy::ALL_PLUGGED_OUTPUTS);
}
// Handle "generate to file"
if (command_line.HasOption(kSaveToFileSwitch)) {
std::string save_file_str;
command_line.GetOptionValue(kSaveToFileSwitch, &save_file_str);
// If just '--wav' is specified, use the default file name.
if (save_file_str == "") {
save_file_str = kSaveToFileDefaultName;
}
media_app.set_save_to_file(true);
media_app.set_save_file_name(save_file_str);
}
media_app.Run(startup_context.get());
// We've set everything going. Wait for our message loop to return.
loop.Run();
return 0;
}