LCOV - code coverage report
Current view: top level - src/media/audio/sound - tone.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 89.8 % 59 53
Test Date: 2026-06-13 09:18:46 Functions: 66.7 % 6 4

            Line data    Source code
       1              : /*
       2              :  *  Copyright (C) 2004-2026 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 "string_utils.h"
      24              : 
      25              : #include <vector>
      26              : #include <cmath>
      27              : #include <cstdlib>
      28              : 
      29              : namespace jami {
      30              : 
      31          380 : Tone::Tone(std::string_view definition, unsigned int sampleRate, AVSampleFormat sampleFormat)
      32          380 :     : AudioLoop(AudioFormat(sampleRate, 1, sampleFormat))
      33              : {
      34          380 :     genBuffer(definition); // allocate memory with definition parameter
      35          380 : }
      36              : 
      37              : struct ParsedDefinition
      38              : {
      39              :     unsigned total_samples;
      40              :     std::vector<std::tuple<unsigned, unsigned, unsigned>> frequencies;
      41              : };
      42              : 
      43              : ParsedDefinition
      44          348 : parseDefinition(std::string_view definition, unsigned sampleRate)
      45              : {
      46          348 :     ParsedDefinition parsed;
      47          348 :     parsed.total_samples = 0;
      48              : 
      49          348 :     std::string_view s; // portion of frequencyq
      50          957 :     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          609 :         size_t pos_plus = s.find('+');
      57          609 :         size_t pos_slash = s.find('/');
      58          609 :         size_t len = s.length();
      59          609 :         size_t endfrequency = 0;
      60              : 
      61          609 :         if (pos_slash == std::string::npos) {
      62           87 :             time = 0;
      63           87 :             endfrequency = len;
      64              :         } else {
      65          522 :             time = to_int<unsigned>(s.substr(pos_slash + 1, len - pos_slash - 1), 0);
      66          522 :             endfrequency = pos_slash;
      67              :         }
      68              : 
      69              :         // without a plus = 1 frequency
      70          609 :         if (pos_plus == std::string::npos) {
      71          261 :             low = to_int<unsigned>(s.substr(0, endfrequency), 0);
      72          261 :             high = 0;
      73              :         } else {
      74          348 :             low = to_int<unsigned>(s.substr(0, pos_plus), 0);
      75          348 :             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          609 :         if (time == 0)
      80           87 :             count = sampleRate;
      81              :         else
      82          522 :             count = (sampleRate * time) / 1000;
      83              : 
      84          609 :         parsed.frequencies.emplace_back(low, high, count);
      85          609 :         parsed.total_samples += count;
      86              :     }
      87          696 :     return parsed;
      88            0 : }
      89              : 
      90              : void
      91          380 : Tone::genBuffer(std::string_view definition)
      92              : {
      93          380 :     if (definition.empty())
      94           32 :         return;
      95              : 
      96          348 :     auto [total_samples, frequencies] = parseDefinition(definition, format_.sample_rate);
      97          348 :     buffer_->nb_samples = total_samples;
      98          348 :     buffer_->format = format_.sampleFormat;
      99          348 :     buffer_->sample_rate = format_.sample_rate;
     100          348 :     av_channel_layout_default(&buffer_->ch_layout, format_.nb_channels);
     101          348 :     av_frame_get_buffer(buffer_.get(), 0);
     102              : 
     103          348 :     size_t outPos = 0;
     104          957 :     for (auto& [low, high, count] : frequencies) {
     105          609 :         genSin(buffer_.get(), outPos, count, low, high);
     106          609 :         outPos += count;
     107              :     }
     108          348 : }
     109              : 
     110              : void
     111         1121 : 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         1121 :     const double sr = (double) buffer->sample_rate;
     115         1121 :     const double dx_h = sr ? PI_2 * lowFrequency / sr : 0.0;
     116         1121 :     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         1121 :     if (buffer->format == AV_SAMPLE_FMT_S16 || buffer->format == AV_SAMPLE_FMT_S16P) {
     121         1121 :         int16_t* ptr = ((int16_t*) buffer->data[0]) + outPos;
     122     20025121 :         for (size_t t = 0; t < nb_samples; t++) {
     123     20024000 :             ptr[t] = DATA_AMPLITUDE_S16 * (sin(t * dx_h) + sin(t * dx_l));
     124              :         }
     125         1121 :     } 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         1121 : }
     134              : 
     135              : } // namespace jami
        

Generated by: LCOV version 2.0-1