text: // ------------------------------------------------------
  // Protrekkr
  // Based on Juan Antonio Arguelles Rius's NoiseTrekker.
  //
  // Copyright (C) 2008-2021 Franck Charlet.
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions
  // are met:
  //
  //  1. Redistributions of source code must retain the above copyright notice,
  //     this list of conditions and the following disclaimer.
  //
  //  2. Redistributions in binary form must reproduce the above copyright notice,
  //     this list of conditions and the following disclaimer in the documentation
  //     and/or other materials provided with the distribution.
  //
  // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
  // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  // ARE DISCLAIMED. IN NO EVENT SHALL FRANCK CHARLET OR CONTRIBUTORS BE LIABLE
  // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  // SUCH DAMAGE.
  // ------------------------------------------------------
  
  // ------------------------------------------------------
  // Includes
  #include "include/sounddriver_aros.h"
  
  // ------------------------------------------------------
  // Variables
  unsigned int AUDIO_To_Fill;
  int AUDIO_Samples;
  int AUDIO_Play_Flag;
  float AUDIO_Timer;
  
  int volatile AUDIO_Acknowledge;
  struct MsgPort *AHImp;
  struct AHIRequest *AHIio;
  struct AHIRequest *AHIio2;
  struct AHIRequest *join;
  short *AHIbuf;
  short *AHIbuf2;
  int volatile Thread_Running;
  
  #include <string.h>
  #if defined(USE_SDL_THREADS)
  #include <SDL/SDL_thread.h>
  SDL_Thread *hThread;
  #else
  #include <pthread.h>
  pthread_t hThread;
  #endif
  
  int AUDIO_SoundBuffer_Size;
  int AUDIO_Latency;
  int AUDIO_Milliseconds = 100;
  
  // ------------------------------------------------------
  // Functions
  int AUDIO_Create_Sound_Buffer(int milliseconds);
  void AUDIO_Stop_Sound_Buffer(void);
  void (STDCALL *AUDIO_Mixer)(Uint8 *, Uint32);
  void AUDIO_Mixer_Fill_Buffer(void *, Uint32);
  void AUDIO_Synth_Play(void);
  
  // ------------------------------------------------------
  // Name: AUDIO_Thread()
  // Desc: Audio rendering
  #if defined(USE_SDL_THREADS)
  int AUDIO_Thread(void *arg)
  #else
  void *AUDIO_Thread(void *arg)
  #endif
  {
      int32 old_sigbit = AHImp->mp_SigBit;
      void *old_sigtask = AHImp->mp_SigTask;
      AHImp->mp_SigBit = AllocSignal(-1);
      AHImp->mp_SigTask = FindTask(NULL);
  
      while(Thread_Running)
      {
          if(AHIbuf)
          {
              struct AHIRequest *io = AHIio;
              short *buf = AHIbuf;
          
              AUDIO_Acknowledge = FALSE;
              if(AUDIO_Play_Flag)
              {
                  AUDIO_Mixer((Uint8 *) buf, AUDIO_SoundBuffer_Size);
              }
              else
              {
                  unsigned int i;
                  char *pSamples = (char *) buf;
                  for(i = 0; i < AUDIO_SoundBuffer_Size; i++)
                  {
                      pSamples[i] = 0;
                  }
                  AUDIO_Acknowledge = TRUE;
              }    
      
              io->ahir_Std.io_Message.mn_Node.ln_Pri = 0;
              io->ahir_Std.io_Command = CMD_WRITE;
              io->ahir_Std.io_Data = buf;
              io->ahir_Std.io_Length = AUDIO_SoundBuffer_Size;
              io->ahir_Std.io_Offset = 0;
              io->ahir_Frequency = AUDIO_PCM_FREQ;
              io->ahir_Type = AHIST_S16S;
              io->ahir_Volume = 0x10000;
              io->ahir_Position = 0x8000;
              io->ahir_Link = join;
              SendIO((struct IORequest *) io);
              if(join) WaitIO((struct IORequest *) join);
              join = io;
              AHIio = AHIio2; AHIio2 = io;
              AHIbuf = AHIbuf2; AHIbuf2 = buf;
          
              AUDIO_Samples += AUDIO_SoundBuffer_Size;
              AUDIO_Timer = ((((float) AUDIO_Samples) * (1.0f / (float) AUDIO_Latency)) * 1000.0f);
          }
          usleep(10);
      }
      if (join)
      {
          AbortIO((struct IORequest *) join);
          WaitIO((struct IORequest *) join);
      }
      FreeSignal(AHImp->mp_SigBit);
      AHImp->mp_SigBit = old_sigbit;
      AHImp->mp_SigTask = old_sigtask;
      Thread_Running = 1;
      
      #if !defined(USE_SDL_THREADS)
          //pthread_exit(0);
      #endif
      return(0);
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_Init_Driver()
  // Desc: Init the audio driver
  int AUDIO_Init_Driver(void (*Mixer)(Uint8 *, Uint32))
  {
      AUDIO_Mixer = Mixer;
  
      AHImp = CreateMsgPort();
  
  #if !defined(__STAND_ALONE__) && !defined(__WINAMP__)
      if(!AHImp)
      {
          Message_Error("Error while calling CreateMsgPort()");
          return(FALSE);
      }
  #endif
  
      AHIio = (struct AHIRequest *) CreateIORequest(AHImp, sizeof(struct AHIRequest));
      AHIio2 = (struct AHIRequest *) CreateIORequest(AHImp, sizeof(struct AHIRequest));
      join = NULL;
      
      // Check ahiios are allocated
      if (!AHIio || !AHIio2)
      {
  
  #if !defined(__STAND_ALONE__) && !defined(__WINAMP__)
          Message_Error("Error while calling CreateIORequest()");
  #endif
  
          return(FALSE);
      }
      
      AHIio->ahir_Version = 4;
      
      // Open ahi
      if (OpenDevice("ahi.device", 0, (struct IORequest *) AHIio, 0))
      {
          AHIio->ahir_Std.io_Device = NULL;
  
  #if !defined(__STAND_ALONE__) && !defined(__WINAMP__)
          Message_Error("Error while calling OpenDevice()");
  #endif
  
          return(FALSE);
      }
      
      // Copy for double buffering
      CopyMem(AHIio, AHIio2, sizeof(struct AHIRequest));
  
      // Create audio buffer
      return(AUDIO_Create_Sound_Buffer(AUDIO_Milliseconds));
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_Create_Sound_Buffer()
  // Desc: Create an audio buffer of given milliseconds
  int AUDIO_Create_Sound_Buffer(int milliseconds)
  {
      int num_fragments;
      int frag_size;
  
      if(milliseconds < 10) milliseconds = 10;
      if(milliseconds > 250) milliseconds = 250;
  
      num_fragments = 6;
      frag_size = (int) (AUDIO_PCM_FREQ * (milliseconds / 1000.0f));
  
  	AUDIO_SoundBuffer_Size = frag_size << 2;
      AUDIO_Latency = AUDIO_SoundBuffer_Size;
  
  	AHIbuf = (short *) AllocVec(AUDIO_SoundBuffer_Size << 1, MEMF_ANY);
  	AHIbuf2 = (short *) AllocVec(AUDIO_SoundBuffer_Size << 1, MEMF_ANY);
  	if (!AHIbuf || !AHIbuf2)
      {
  
  #if !defined(__STAND_ALONE__) && !defined(__WINAMP__)
          Message_Error("Error while calling AllocVec()");
  #endif
  
  		return(FALSE);
  	}
  
      Thread_Running = 1;
      
  #if defined(USE_SDL_THREADS)
      if(hThread = SDL_CreateThread(AUDIO_Thread, NULL))
  #else
      if(pthread_create(&hThread, NULL, AUDIO_Thread, NULL) == 0)
  #endif
      {
          return(TRUE);
      }
  
  #if !defined(__STAND_ALONE__) && !defined(__WINAMP__)
      Message_Error("Error while calling pthread_create()");
  #endif
  
      Thread_Running = 0;
      return(FALSE);
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_Wait_For_Thread()
  // Desc: Wait for a command acknowledgment from the thread
  void AUDIO_Wait_For_Thread(void)
  {
      if(Thread_Running)
      {
          if(AUDIO_Play_Flag)
          {
              while(AUDIO_Acknowledge)
              {
                  usleep(10);
              };
          }
          else
          {
          #if defined(USE_SDL_THREADS)
              if(hThread)     // sdl thread
          #else
              if(hThread.p)   // pthreads
          #endif
              {
                  while(!AUDIO_Acknowledge)
                  {
                      usleep(10);
                  };
              }
          }
      }
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_Play()
  // Desc: Play the sound buffer endlessly
  void AUDIO_Play(void)
  {
      AUDIO_ResetTimer();
      AUDIO_Play_Flag = TRUE;
      AUDIO_Wait_For_Thread();
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_IsPlaying()
  // Desc: Return the playing state of the sound buffer
  int AUDIO_IsPlaying(void)
  {
      return(AUDIO_Play_Flag);
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_ResetTimer()
  // Desc: Reset the samples counter
  void AUDIO_ResetTimer(void)
  {
      AUDIO_Samples = 0;
      AUDIO_Timer = 0.0f;
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_GetTime()
  // Desc: Return the played time in milliseconds
  float AUDIO_GetTime(void)
  {
      return(AUDIO_Timer);
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_GetSamples()
  // Desc: Return the played time in milliseconds
  int AUDIO_GetSamples(void)
  {
      return(AUDIO_Samples);
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_Stop()
  // Desc: Stop the sound buffer
  void AUDIO_Stop(void)
  {
      AUDIO_Play_Flag = FALSE;
      AUDIO_Wait_For_Thread();
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_Stop_Sound_Buffer()
  // Desc: Release the audio buffer
  void AUDIO_Stop_Sound_Buffer(void)
  {
      AUDIO_Stop();
  
      #if defined(USE_SDL_THREADS)
          if(hThread) //sdl thread
      #else
          if(hThread.p) //pthreads
      #endif
      {
          Thread_Running = 0;
          while(!Thread_Running)
          {
              usleep(10);
          }
      }
      FreeVec(AHIbuf);
      FreeVec(AHIbuf2);
      AHIbuf = NULL;
      AHIbuf2 = NULL;
  }
  
  // ------------------------------------------------------
  // Name: AUDIO_Stop_Driver()
  // Desc: Stop everything
  void AUDIO_Stop_Driver(void)
  {
      AUDIO_Stop_Sound_Buffer();
  
      if (AHIio && AHIio->ahir_Std.io_Device)
      {
          CloseDevice((struct IORequest *) AHIio);
      }
      DeleteIORequest((struct IORequest *) AHIio);
      DeleteIORequest((struct IORequest *) AHIio2);
      DeleteMsgPort(AHImp);
      AHIio = NULL;
      AHIio2 = NULL;
      AHImp = NULL;
  }
  