LCOV - code coverage report
Current view: top level - src/media/audio/sound - tone.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 53 59 89.8 %
Date: 2024-12-21 08:56:24 Functions: 4 6 66.7 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  Inspired by tonegenerator of
       5             :  *   Laurielle Lea <laurielle.lea@savoirfairelinux.com> (2004)
       6             :  *  Inspired by ringbuffer of Audacity Project
       7             :  *
       8             :  *  This program is free software: you can redistribute it and/or modify
       9             :  *  it under the terms of the GNU General Public License as published by
      10             :  *  the Free Software Foundation, either version 3 of the License, or
      11             :  *  (at your option) any later version.
      12             :  *
      13             :  *  This program is distributed in the hope that it will be useful,
      14             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      16             :  *  GNU General Public License for more details.
      17             :  *
      18             :  *  You should have received a copy of the GNU General Public License
      19             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      20             :  */
      21             : #include "tone.h"
      22             : #include "logger.h"
      23             : #include "ring_types.h"
      24             : #include "string_utils.h"
      25             : 
      26             : #include <vector>
      27             : #include <cmath>
      28             : #include <cstdlib>
      29             : 
      30             : namespace jami {
      31             : 
      32         397 : Tone::Tone(std::string_view definition, unsigned int sampleRate, AVSampleFormat sampleFormat)
      33         397 :     : AudioLoop(AudioFormat(sampleRate, 1, sampleFormat))
      34             : {
      35         397 :     genBuffer(definition); // allocate memory with definition parameter
      36         397 : }
      37             : 
      38             : struct ParsedDefinition {
      39             :     unsigned total_samples;
      40             :     std::vector<std::tuple<unsigned, unsigned, unsigned>> frequencies;
      41             : };
      42             : 
      43             : ParsedDefinition
      44         364 : parseDefinition(std::string_view definition, unsigned sampleRate)
      45             : {
      46         364 :     ParsedDefinition parsed;
      47         364 :     parsed.total_samples = 0;
      48             : 
      49         364 :     std::string_view s; // portion of frequencyq
      50        1001 :     while (getline_full(definition, s, ',')) {
      51             :         // Sample string: "350+440" or "350+440/2000,244+655/2000"
      52             :         unsigned low, high, time;
      53             :         size_t count;  // number of int for one sequence
      54             : 
      55             :         // The 1st frequency is before the first + or the /
      56         637 :         size_t pos_plus = s.find('+');
      57         637 :         size_t pos_slash = s.find('/');
      58         637 :         size_t len = s.length();
      59         637 :         size_t endfrequency = 0;
      60             : 
      61         637 :         if (pos_slash == std::string::npos) {
      62          91 :             time = 0;
      63          91 :             endfrequency = len;
      64             :         } else {
      65         546 :             time = to_int<unsigned>(s.substr(pos_slash + 1, len - pos_slash - 1), 0);
      66         546 :             endfrequency = pos_slash;
      67             :         }
      68             : 
      69             :         // without a plus = 1 frequency
      70         637 :         if (pos_plus == std::string::npos) {
      71         273 :             low = to_int<unsigned>(s.substr(0, endfrequency), 0);
      72         273 :             high = 0;
      73             :         } else {
      74         364 :             low = to_int<unsigned>(s.substr(0, pos_plus), 0);
      75         364 :             high = to_int<unsigned>(s.substr(pos_plus + 1, endfrequency - pos_plus - 1), 0);
      76             :         }
      77             : 
      78             :         // If there is time or if it's unlimited
      79         637 :         if (time == 0)
      80          91 :             count = sampleRate;
      81             :         else
      82         546 :             count = (sampleRate * time) / 1000;
      83             : 
      84         637 :         parsed.frequencies.emplace_back(low, high, count);
      85         637 :         parsed.total_samples += count;
      86             :     }
      87         728 :     return parsed;
      88           0 : }
      89             : 
      90             : void
      91         397 : Tone::genBuffer(std::string_view definition)
      92             : {
      93         397 :     if (definition.empty())
      94          33 :         return;
      95             : 
      96         364 :     auto [total_samples, frequencies] = parseDefinition(definition, format_.sample_rate);
      97         364 :     buffer_->nb_samples = total_samples;
      98         364 :     buffer_->format = format_.sampleFormat;
      99         364 :     buffer_->sample_rate = format_.sample_rate;
     100         364 :     av_channel_layout_default(&buffer_->ch_layout, format_.nb_channels);
     101         364 :     av_frame_get_buffer(buffer_.get(), 0);
     102             : 
     103         364 :     size_t outPos = 0;
     104        1001 :     for (auto& [low, high, count] : frequencies) {
     105         637 :         genSin(buffer_.get(), outPos, count, low, high);
     106         637 :         outPos += count;
     107             :     }
     108         364 : }
     109             : 
     110             : void
     111        1165 : Tone::genSin(AVFrame* buffer, size_t outPos, unsigned nb_samples, unsigned lowFrequency, unsigned highFrequency)
     112             : {
     113             :     static constexpr auto PI_2 = 3.141592653589793238462643383279502884L * 2.0L;
     114        1165 :     const double sr = (double) buffer->sample_rate;
     115        1165 :     const double dx_h = sr ? PI_2 * lowFrequency / sr : 0.0;
     116        1165 :     const double dx_l = sr ? PI_2 * highFrequency / sr : 0.0;
     117             :     static constexpr double DATA_AMPLITUDE_S16 = 2048;
     118             :     static constexpr double DATA_AMPLITUDE_FLT = 0.0625;
     119             : 
     120        1165 :     if (buffer->format == AV_SAMPLE_FMT_S16 || buffer->format == AV_SAMPLE_FMT_S16P) {
     121        1165 :         int16_t* ptr = ((int16_t*) buffer->data[0]) + outPos;
     122    20825165 :         for (size_t t = 0; t < nb_samples; t++) {
     123    20824000 :             ptr[t] = DATA_AMPLITUDE_S16 * (sin(t * dx_h) + sin(t * dx_l));
     124             :         }
     125        1165 :     } else if (buffer->format == AV_SAMPLE_FMT_FLT || buffer->format == AV_SAMPLE_FMT_FLTP) {
     126           0 :         float* ptr = ((float*) buffer->data[0]) + outPos;
     127           0 :         for (size_t t = 0; t < nb_samples; t++) {
     128           0 :             ptr[t] = (sin(t * dx_h) + sin(t * dx_l)) * DATA_AMPLITUDE_FLT;
     129             :         }
     130           0 :     } else {
     131           0 :         JAMI_ERROR("Unsupported sample format: {}", av_get_sample_fmt_name((AVSampleFormat)buffer->format));
     132             :     }
     133        1165 : }
     134             : 
     135             : } // namespace jami

Generated by: LCOV version 1.14