blob: 96d74c97913d4beaa5755d35d3bca602e2a462da [file] [log] [blame]
// Copyright 2010 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/tone_generators.h"
#include <math.h>
#include <stdio.h>
#include <limits>
namespace {
static const double kPi = 3.14159265358979323846264338327l;
static const double kHalfPi = kPi / 2.0;
} // namespace
SineWaveGenerator::SineWaveGenerator(int sample_rate,
double length_sec,
int volume_gain)
: cur_x_(0.0),
cur_frame_(0),
sample_rate_(sample_rate),
frequency_(0.0),
volume_gain_(volume_gain) {
if (length_sec > 0)
total_frame_ = length_sec * sample_rate;
else
total_frame_ = 0;
}
double SineWaveGenerator::Next() {
cur_x_ += (kPi * 2 * frequency_) / sample_rate_;
cur_frame_++;
return sin(cur_x_) * volume_gain_ / 100.0;
}
void SineWaveGenerator::Reset(double frequency) {
cur_x_ = 0;
cur_frame_ = 0;
frequency_ = frequency;
}
size_t SineWaveGenerator::GetFrames(SampleFormat format,
int num_channels,
const std::set<int>& active_channels,
void* data,
size_t buf_size) {
int remain_frames = total_frame_ > 0
? (static_cast<int>(total_frame_) - cur_frame_ - 1)
: std::numeric_limits<int>::max();
int frame_required = buf_size / num_channels / format.bytes();
int num_frames = std::min(frame_required, remain_frames);
for (int i = 0; i < num_frames; ++i) {
double sample = Next();
for (int c = 0; c < num_channels; ++c) {
if (active_channels.find(c) != active_channels.end())
data = WriteSample(sample, format, data);
else
data = WriteSample(0.0f, format, data);
}
}
return num_frames;
}
bool SineWaveGenerator::HasMoreFrames() const {
if (total_frame_ > 0) {
return cur_frame_ < total_frame_;
}
// Infinite.
return true;
}
MultiToneGenerator::MultiToneGenerator(int sample_rate, double length_sec)
: frames_generated_(0),
frames_wanted_(length_sec * sample_rate),
fade_frames_(0), // Calculated below.
sample_rate_(sample_rate),
cur_vol_(1.0),
start_vol_(1.0),
inc_vol_(0.0) {
// Use a fade of 2.5ms at both the start and end of a tone .
const double kFadeTimeSec = 0.005;
// Only fade if the fade won't take more than 1/2 the tone.
if (length_sec > (kFadeTimeSec * 4)) {
fade_frames_ = kFadeTimeSec * sample_rate;
}
frequencies_.clear();
pthread_mutex_init(&param_mutex, NULL);
}
MultiToneGenerator::~MultiToneGenerator() {
pthread_mutex_destroy(&param_mutex);
}
void MultiToneGenerator::SetVolumes(double start_vol, double end_vol) {
pthread_mutex_lock(&param_mutex);
cur_vol_ = start_vol_ = start_vol;
inc_vol_ = (end_vol - start_vol) / frames_wanted_;
pthread_mutex_unlock(&param_mutex);
}
void MultiToneGenerator::Reset(const std::vector<double>& frequencies,
bool reset_timer) {
pthread_mutex_lock(&param_mutex);
frequencies_ = frequencies;
if (reset_timer) {
frames_generated_ = 0;
cur_vol_ = start_vol_;
}
pthread_mutex_unlock(&param_mutex);
}
void MultiToneGenerator::Reset(const double* frequency,
int num_tones,
bool reset_timer) {
pthread_mutex_lock(&param_mutex);
frequencies_.resize(num_tones);
for (int i = 0; i < num_tones; ++i) {
frequencies_[i] = frequency[i];
}
if (reset_timer) {
frames_generated_ = 0;
cur_vol_ = start_vol_;
}
pthread_mutex_unlock(&param_mutex);
}
void MultiToneGenerator::Reset(double frequency, bool reset_timer) {
pthread_mutex_lock(&param_mutex);
frequencies_.resize(1);
frequencies_[0] = frequency;
if (reset_timer) {
frames_generated_ = 0;
cur_vol_ = start_vol_;
}
pthread_mutex_unlock(&param_mutex);
}
size_t MultiToneGenerator::GetFrames(SampleFormat format,
int num_channels,
const std::set<int>& active_channels,
void* data,
size_t buf_size) {
const int kBytesPerFrame = num_channels * format.bytes();
void* cur = data;
int frames = buf_size / kBytesPerFrame;
int frames_written;
pthread_mutex_lock(&param_mutex);
tone_wave_.resize(frequencies_.size(), SineWaveGenerator(sample_rate_));
for (size_t f = 0; f < frequencies_.size(); ++f)
tone_wave_[f].Reset(frequencies_[f]);
for (frames_written = 0; frames_written < frames; ++frames_written) {
if (!HasMoreFrames()) {
break;
}
double frame_magnitude = 0;
for (size_t f = 0; f < frequencies_.size(); ++f) {
frame_magnitude += tone_wave_[f].Next();
}
frame_magnitude *= GetFadeMagnitude() * cur_vol_;
if (frequencies_.size() > 1) {
frame_magnitude /= static_cast<double>(frequencies_.size());
}
cur_vol_ += inc_vol_;
for (int c = 0; c < num_channels; ++c) {
if (active_channels.find(c) != active_channels.end()) {
cur = WriteSample(frame_magnitude, format, cur);
} else {
// Silence the non-active channels.
cur = WriteSample(0.0f, format, cur);
}
}
++frames_generated_;
}
pthread_mutex_unlock(&param_mutex);
return frames_written * kBytesPerFrame;
}
bool MultiToneGenerator::HasMoreFrames() const {
return frames_generated_ < frames_wanted_;
}
double MultiToneGenerator::GetFadeMagnitude() const {
int frames_left = frames_wanted_ - frames_generated_;
if (frames_generated_ < fade_frames_) { // Fade in.
return sin(kHalfPi * frames_generated_ / fade_frames_);
} else if (frames_left < fade_frames_) { // Fade out.
return sin(kHalfPi * frames_left / fade_frames_);
} else {
return 1.0f;
}
}
// A# minor harmoic scale is: A#, B# (C), C#, D#, E# (F), F#, G## (A).
const double ASharpMinorGenerator::kNoteFrequencies[] = {
466.16, 523.25, 554.37, 622.25, 698.46, 739.99, 880.00, 932.33,
932.33, 880.00, 739.99, 698.46, 622.25, 554.37, 523.25, 466.16,
};
ASharpMinorGenerator::ASharpMinorGenerator(int sample_rate,
double tone_length_sec)
: tone_generator_(sample_rate, tone_length_sec), cur_note_(0) {
tone_generator_.Reset(kNoteFrequencies[cur_note_], true);
}
ASharpMinorGenerator::~ASharpMinorGenerator() {}
void ASharpMinorGenerator::SetVolumes(double start_vol, double end_vol) {
tone_generator_.SetVolumes(start_vol, end_vol);
}
void ASharpMinorGenerator::Reset() {
cur_note_ = 0;
tone_generator_.Reset(kNoteFrequencies[cur_note_], true);
}
size_t ASharpMinorGenerator::GetFrames(SampleFormat format,
int num_channels,
const std::set<int>& active_channels,
void* data,
size_t buf_size) {
if (!HasMoreFrames()) {
return 0;
}
// Go to next note if necessary.
if (!tone_generator_.HasMoreFrames()) {
tone_generator_.Reset(kNoteFrequencies[++cur_note_], true);
}
return tone_generator_.GetFrames(format, num_channels, active_channels, data,
buf_size);
}
bool ASharpMinorGenerator::HasMoreFrames() const {
return cur_note_ < kNumNotes - 1 || tone_generator_.HasMoreFrames();
}