
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//
//

#ifndef BEEP_MANAGER_HH
#define BEEP_MANAGER_HH
 
#include "runnable.hh"
#include <alsa/asoundlib.h>
#include "beep_job.hh"


class beep_manager : public runnable
{
  // The device "hw:0,0" works on the ZCU111, where "default" does not!
  constexpr const static char*  device1                  = "hw:0,0";
  constexpr const static char*  device2                  = "default";
  const static           int    num_channels             = 2;
  const static           int    sample_rate_hz           = 48000; // Supported by HDMI audio better
  const static           int    requested_buffer_length  = 4096;  // Not too long, or there is an audible delay
  const static           int    requested_num_periods    = 2;       // Divide the buffer into two halves

  int num_periods;
  int buffer_length;
  int period_length;  // A buffer is evenly divided into multiple "periods"

  // zero_data is played when there is no job.  It has length period_length.
  channels* zero_data;

  snd_pcm_t*           handle;
  snd_pcm_hw_params_t* hwparams;
  snd_pcm_sw_params_t* swparams;

  //
  // Only one job at a time.  If a second job is tasked while one is going on,
  // the second one is ignored.  If two are started at about the same time,
  // one is started at random.  The job is set by making next_job nonzero,
  // by pointing it at a job.  The process then picks that up and performs the
  // job, ignoring any changes to next_job.  When finished, it zeros next_job,
  // waiting for it to be set again.  In this way, it is asynchronous with
  // a thread-safe methodology, without explicit synchronization semaphores.
  //
  beep_job* next_job;

  bool start_audio();
  bool send_job(channels* data, int length);

  void run();

public:
  int  get_sample_rate_hz()        { return sample_rate_hz;  }
  int  get_num_channels()          { return num_channels;    }
  int  get_buffer_length()         { return buffer_length;   }
  int  get_period_length()         { return period_length;   }

  // Do the beep job if no other beep job conflicts.
  // The data in the job should be persistent.
  void do_beep_job(beep_job* job)  { next_job = job;        }

  beep_manager();
  ~beep_manager();
};


// There can be only one audio_beep_manager, obtained by calling this
// function using the "construct on first use idiom"
beep_manager* audio_beep_manager();

 
//
// Old scraps from a version that required synchronization between threads.
//
//#include "synchronized_data.h"
//synchronized_data<bool>        beep_command;
//static synchronized_data<bool> audio_exclusion;


#endif
