summaryrefslogtreecommitdiff
path: root/apps/dsp_asm.h
blob: 7bf18370a357abdee3d3c6fb8cdc5cf45225238a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2006 Thom Johansen
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include <config.h>

#ifndef _DSP_ASM_H
#define _DSP_ASM_H

/* Set the appropriate #defines based on CPU or whatever matters */
#if defined(CPU_ARM)
#define DSP_HAVE_ASM_APPLY_GAIN
#define DSP_HAVE_ASM_RESAMPLING
#define DSP_HAVE_ASM_CROSSFEED
#define DSP_HAVE_ASM_SOUND_CHAN_MONO
#define DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
#define DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
#define DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
#define DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
#elif defined (CPU_COLDFIRE)
#define DSP_HAVE_ASM_APPLY_GAIN
#define DSP_HAVE_ASM_RESAMPLING
#define DSP_HAVE_ASM_CROSSFEED
#define DSP_HAVE_ASM_SOUND_CHAN_MONO
#define DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
#define DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
#define DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
#define DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
#endif /* CPU_COLDFIRE */

/* Declare prototypes based upon what's #defined above */
#ifdef DSP_HAVE_ASM_CROSSFEED
void apply_crossfeed(int count, int32_t *buf[]);
#endif

#ifdef DSP_HAVE_ASM_APPLY_GAIN
void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]);
#endif /* DSP_HAVE_ASM_APPLY_GAIN* */

#ifdef DSP_HAVE_ASM_RESAMPLING
int dsp_upsample(int count, struct dsp_data *data,
                 const int32_t *src[], int32_t *dst[]);
int dsp_downsample(int count, struct dsp_data *data,
                   const int32_t *src[], int32_t *dst[]);
#endif /* DSP_HAVE_ASM_RESAMPLING */

#ifdef DSP_HAVE_ASM_SOUND_CHAN_MONO
void channels_process_sound_chan_mono(int count, int32_t *buf[]);
#endif

#ifdef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
void channels_process_sound_chan_custom(int count, int32_t *buf[]);
#endif

#ifdef DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
void channels_process_sound_chan_karaoke(int count, int32_t *buf[]);
#endif

#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
void sample_output_stereo(int count, struct dsp_data *data,
                          const int32_t *src[], int16_t *dst);
#endif

#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
void sample_output_mono(int count, struct dsp_data *data,
                        const int32_t *src[], int16_t *dst);
#endif

#endif /* _DSP_ASM_H */
id='n315' href='#n315'>315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 *
 *  PrBoom a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program 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.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:
 *  System interface for sound.
 *
 *-----------------------------------------------------------------------------
 */

#include "z_zone.h"

#include "i_system.h"
#include "i_sound.h"
#include "m_argv.h"
#include "m_misc.h"
#include "w_wad.h"
#include "m_swap.h"
#include "d_main.h"
#include "doomdef.h"
#include "rockmacros.h"

// The number of internal mixing channels,
//  the samples calculated for each mixing step,
//  the size of the 16bit, 2 hardware channel (stereo)
//  mixing buffer, and the samplerate of the raw data.

// Needed for calling the actual sound output.
#define SAMPLECOUNT     128

#define NUM_CHANNELS    24
// It is 2 for 16bit, and 2 for two channels.
#define BUFMUL          2
#define MIXBUFFERSIZE  (SAMPLECOUNT*BUFMUL)

#ifdef HW_HAVE_11
#define SAMPLERATE  SAMPR_11 // 44100 22050 11025
#else
#define SAMPLERATE  SAMPR_44 // 44100 22050 11025
#endif
#define SAMPLESIZE  2      // 16bit

// The global mixing buffer.
//  Basically, samples from all active internal channels
//  are modifed and added, and stored in the buffer
//  that is submitted to the audio device.
signed short *mixbuffer;
/* Don't place this in IRAM!
 * Sound playback uses DMA, and not all IRAM is DMA capable on coldfire. */

typedef struct {
   // SFX id of the playing sound effect.
   // Used to catch duplicates (like chainsaw).
   int id;
   // The channel step amount...
   unsigned int step;
   // ... and a 0.16 bit remainder of last step.
   unsigned int stepremainder;
   unsigned int samplerate;
   // The channel data pointers, start and end.
   const unsigned char* data;
   const unsigned char* enddata;
   // Time/gametic that the channel started playing,
   //  used to determine oldest, which automatically
   //  has lowest priority.
   // In case number of active sounds exceeds
   //  available channels.
   int starttime;
   // Hardware left and right channel volume lookup.
   int *leftvol_lookup;
   int *rightvol_lookup;
} channel_info_t;

channel_info_t channelinfo[NUM_CHANNELS] IBSS_ATTR;

int *vol_lookup; // Volume lookups.

int   *steptable; // Pitch to stepping lookup. (Not setup properly right now)

static inline int32_t clip_sample16(int32_t sample)
{
    if ((int16_t)sample != sample)
        sample = 0x7fff ^ (sample >> 31);
    return sample;
}

//
// This function loads the sound data from the WAD lump for single sound.
// It is used to cache all the sounddata at startup.
//
void* getsfx( const char* sfxname )
{
   unsigned char*      sfx;
   unsigned char*      paddedsfx;
   int                 size;
   char                name[20];
   int                 sfxlump;

   // Get the sound data from the WAD, allocate lump
   //  in zone memory.
   snprintf(name, sizeof(name), "ds%s", sfxname);

   // Now, there is a severe problem with the sound handling, in it is not
   //  (yet/anymore) gamemode aware. That means, sounds from DOOM II will be
   //  requested even with DOOM shareware.
   // The sound list is wired into sounds.c, which sets the external variable.
   // I do not do runtime patches to that variable. Instead, we will use a
   //  default sound for replacement.
   if ( W_CheckNumForName(name) == -1 )
      sfxlump = W_GetNumForName("dspistol");
   else
      sfxlump = W_GetNumForName(name);

   size = W_LumpLength( sfxlump );

   sfx = (unsigned char*)W_CacheLumpNum( sfxlump);

   paddedsfx = (unsigned char*)malloc( size );  // Allocate from memory.
   memcpy(paddedsfx, sfx, size );               // Now copy and pad.
   W_UnlockLumpNum(sfxlump);    // Remove the cached lump.

   return (void *) (paddedsfx); // Return allocated data.
}

/* cph
 * stopchan
 * Stops a sound
 */
static void stopchan(int i)
{
    channelinfo[i].data=NULL;
}

//
// This function adds a sound to the
//  list of currently active sounds,
//  which is maintained as a given number
//  (eight, usually) of internal channels.
// Returns a handle.
//
int addsfx( int  sfxid, int  channel)
{
   stopchan(channel);

   // We will handle the new SFX.
   // Set pointer to raw data.
   {
      int lump = S_sfx[sfxid].lumpnum;
      size_t len = W_LumpLength(lump);

      /* Find padded length */
      len -= 8;
      channelinfo[channel].data = S_sfx[sfxid].data;

      /* Set pointer to end of raw data. */
      channelinfo[channel].enddata = channelinfo[channel].data + len - 1;
      channelinfo[channel].samplerate = (channelinfo[channel].data[3]<<8)+channelinfo[channel].data[2];
      channelinfo[channel].data += 8; /* Skip header */
   }

   channelinfo[channel].stepremainder = 0;
   // Should be gametic, I presume.
   channelinfo[channel].starttime = gametic;

    // Preserve sound SFX id,
    //  e.g. for avoiding duplicates of chainsaw.
   channelinfo[channel].id = sfxid;

   return channel;
}

static void updateSoundParams(int handle, int volume, int seperation, int pitch)
{
   int rightvol;
   int leftvol;
   int slot = handle;
   int step = steptable[pitch];
#ifdef RANGECHECK
   if (handle>=NUM_CHANNELS)
      I_Error("I_UpdateSoundParams: handle out of range");
#endif
   // Set stepping
   // MWM 2000-12-24: Calculates proportion of channel samplerate
   // to global samplerate for mixing purposes.
   // Patched to shift left *then* divide, to minimize roundoff errors
   // as well as to use SAMPLERATE as defined above, not to assume 11025 Hz
   if (pitched_sounds)
      channelinfo[slot].step = step + (((channelinfo[slot].samplerate<<16)/SAMPLERATE)-65536);
   else
      channelinfo[slot].step = ((channelinfo[slot].samplerate<<16)/SAMPLERATE);

   // Separation, that is, orientation/stereo.
   //  range is: 1 - 256
   seperation += 1;

   // Per left/right channel.
   //  x^2 seperation,
   //  adjust volume properly.
   leftvol = volume - ((volume*seperation*seperation) >> 16);
   seperation = seperation - 257;
   rightvol= volume - ((volume*seperation*seperation) >> 16);

   // Sanity check, clamp volume.
   if (rightvol < 0 || rightvol > 127)
      I_Error("rightvol out of bounds");

   if (leftvol < 0 || leftvol > 127)
      I_Error("leftvol out of bounds");

   // Get the proper lookup table piece
   //  for this volume level???
   channelinfo[slot].leftvol_lookup = &vol_lookup[leftvol*256];
   channelinfo[slot].rightvol_lookup = &vol_lookup[rightvol*256];
}

void I_UpdateSoundParams(int handle, int volume, int seperation, int pitch)
{
  updateSoundParams(handle, volume, seperation, pitch);
}

//
// SFX API
// Note: this was called by S_Init.
// However, whatever they did in the
// old DPMS based DOS version, this
// were simply dummies in the Linux
// version.
// See soundserver initdata().
//
void I_SetChannels()
{
   // Init internal lookups (raw data, mixing buffer, channels).
   // This function sets up internal lookups used during
   //  the mixing process.
   int  i;
   int  j;
   int*  steptablemid = steptable + 128;

   // Okay, reset internal mixing channels to zero.
   for (i=0; i<NUM_CHANNELS; i++)
      memset(&channelinfo[i],0,sizeof(channel_info_t));

   // This table provides step widths for pitch parameters.
   for (i=-128 ; i<128 ; i++)
      steptablemid[i]=2;
//      steptablemid[i] = (int)(pow(1.2, ((double)i/(64.0*SAMPLERATE/11025)))*65536.0);

   // Generates volume lookup tables
   //  which also turn the unsigned samples
   //  into signed samples.
   for (i=0 ; i<128 ; i++)
      for (j=0 ; j<256 ; j++)
         vol_lookup[i*256+j] = 3*(i*(j-128)*256)/191;
}

void I_SetSfxVolume(int volume)
{
   // Identical to DOS.
   // Basically, this should propagate
   //  the menu/config file setting
   //  to the state variable used in
   //  the mixing.
   snd_SfxVolume = volume;
}

// MUSIC API - dummy. Some code from DOS version.
void I_SetMusicVolume(int volume)
{
   // Internal state variable.
   snd_MusicVolume = volume;
   // Now set volume on output device.
   // Whatever( snd_MusciVolume );
}

//
// Retrieve the raw data lump index
//  for a given SFX name.
//
int I_GetSfxLumpNum(sfxinfo_t* sfx)
{
   char namebuf[9];
   snprintf(namebuf, sizeof(namebuf), "ds%s", sfx->name);
   return W_GetNumForName(namebuf);
}

//
// Starting a sound means adding it
//  to the current list of active sounds
//  in the internal channels.
// As the SFX info struct contains
//  e.g. a pointer to the raw data,
//  it is ignored.
// As our sound handling does not handle
//  priority, it is ignored.
// Pitching (that is, increased speed of playback)
//  is set, but currently not used by mixing.
//
int I_StartSound(int id, int channel, int vol, int sep, int pitch, int priority)
{
   (void)priority;
   int handle;

   // Returns a handle (not used).
   handle = addsfx(id,channel);

#ifdef RANGECHECK
   if (handle>=NUM_CHANNELS)
      I_Error("I_StartSound: handle out of range");
#endif
   updateSoundParams(handle, vol, sep, pitch);

   return handle;
}

void I_StopSound (int handle)
{
#ifdef RANGECHECK
   if (handle>=NUM_CHANNELS)
      I_Error("I_StopSound: handle out of range");
#endif
   stopchan(handle);
}

int I_SoundIsPlaying(int handle)
{
#ifdef RANGECHECK
   if (handle>=NUM_CHANNELS)
      I_Error("I_SoundIsPlaying: handle out of range");
#endif
   return channelinfo[handle].data != NULL;
}

//
// This function loops all active (internal) sound
//  channels, retrieves a given number of samples
//  from the raw sound data, modifies it according
//  to the current (internal) channel parameters,
//  mixes the per channel samples into the given
//  mixing buffer, and clamping it to the allowed
//  range.
//
// This function currently supports only 16bit.
//

void I_UpdateSound( void )
{
   // Mix current sound data.
   // Data, from raw sound, for right and left.
   register unsigned char sample;
   register int  dl;
   register int  dr;

   // Pointers in global mixbuffer, left, right, end.
   signed short*  leftout;
   signed short*  rightout;
   signed short*  leftend;

   // Step in mixbuffer, left and right, thus two.
   const int step = 2;

   // Mixing channel index.
   int    chan;

   // Left and right channel
   //  are in global mixbuffer, alternating.
   leftout = mixbuffer;
   rightout = mixbuffer +1;

   // Determine end, for left channel only
   //  (right channel is implicit).
   leftend = mixbuffer + SAMPLECOUNT*step;

   // Mix sounds into the mixing buffer.
   // Loop over step*SAMPLECOUNT,
   //  that is 512 values for two channels.
   while (leftout != leftend)
   {
      // Reset left/right value.
      dl = 0;
      dr = 0;

      // Love thy L2 chache - made this a loop.
      // Now more channels could be set at compile time
      //  as well. Thus loop those  channels.
      for ( chan = 0; chan < NUM_CHANNELS; chan++ )
      {
         // Check channel, if active.
         if (channelinfo[chan].data)
         {
            // Get the raw data from the channel.
            sample = (((unsigned int)channelinfo[chan].data[0] * (0x10000 - channelinfo[chan].stepremainder))
                     + ((unsigned int)channelinfo[chan].data[1] * (channelinfo[chan].stepremainder))) >> 16;
            // Add left and right part
            //  for this channel (sound)
            //  to the current data.
            // Adjust volume accordingly.
            dl += channelinfo[chan].leftvol_lookup[sample];
            dr += channelinfo[chan].rightvol_lookup[sample];
            // Increment index ???
            channelinfo[chan].stepremainder += channelinfo[chan].step;
            // MSB is next sample???
            channelinfo[chan].data += channelinfo[chan].stepremainder >> 16;
            // Limit to LSB???
            channelinfo[chan].stepremainder &= 0xffff;

            // Check whether we are done.
            if (channelinfo[chan].data >= channelinfo[chan].enddata)
               stopchan(chan);
         }
      }

      // Clamp to range. Left hardware channel.
      // Has been char instead of short.
      // if (dl > 127) *leftout = 127;
      // else if (dl < -128) *leftout = -128;
      // else *leftout = dl;

      *leftout = clip_sample16(dl);

      // Same for right hardware channel.
      *rightout = clip_sample16(dr);

      // Increment current pointers in mixbuffer.
      leftout += step;
      rightout += step;
   }
}

//
// This would be used to write out the mixbuffer
//  during each game loop update.
// Updates sound buffer and audio device at runtime.
// It is called during Timer interrupt with SNDINTR.
// Mixing now done synchronous, and
//  only output be done asynchronous?
//

void get_more(unsigned char** start, size_t* size)
{
   I_UpdateSound(); // Force sound update

   *start = (unsigned char*)(mixbuffer);
   *size = SAMPLECOUNT*2*sizeof(short);
}


void I_SubmitSound(void)
{
   if (!enable_sound)
      return;

   rb->pcm_play_data(&get_more, NULL, 0);
}

void I_ShutdownSound(void)
{
   rb->pcm_play_stop();
   rb->pcm_set_frequency(HW_SAMPR_DEFAULT); // 44100
}

void I_InitSound()
{
   int i;

   // Initialize external data (all sounds) at start, keep static.
   printf( "I_InitSound: ");
   rb->pcm_play_stop();

#if INPUT_SRC_CAPS != 0
   /* Select playback */
   rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
   rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
#endif
   rb->pcm_set_frequency(SAMPLERATE);

   vol_lookup=malloc(128*256*sizeof(int));

   mixbuffer=malloc(MIXBUFFERSIZE*sizeof(short));
   steptable=malloc(256*sizeof(int));

   for (i=1 ; i<NUMSFX ; i++)
   {
      if (!S_sfx[i].link) // Alias? Example is the chaingun sound linked to pistol.
         S_sfx[i].data = getsfx( S_sfx[i].name); // Load data from WAD file.
      else
         S_sfx[i].data = S_sfx[i].link->data; // Previously loaded already?
   }

   printf( " pre-cached all sound data\n");

   // Now initialize mixbuffer with zero.
   for ( i = 0; i< MIXBUFFERSIZE; i++ )
      mixbuffer[i] = 0;

   // Finished initialization.
   printf("I_InitSound: sound module ready\n");
}

//
// MUSIC API.
// Still no music done.
// Remains. Dummies.
//
void I_InitMusic(void)  {
}
void I_ShutdownMusic(void) {
}

static int looping=0;
static int musicdies=-1;

void I_PlaySong(int handle, int looping)
{
   // UNUSED.
   handle = looping = 0;
   musicdies = gametic + TICRATE*30;
}

void I_PauseSong (int handle)
{
   // UNUSED.
   handle = 0;
}

void I_ResumeSong (int handle)
{
   // UNUSED.
   handle = 0;
}

void I_StopSong(int handle)
{
   // UNUSED.
   handle = 0;

   looping = 0;
   musicdies = 0;
}

void I_UnRegisterSong(int handle)
{
   // UNUSED.
   handle = 0;
}

int I_RegisterSong(const void *data)
{
   // UNUSED.
   data = NULL;

   return 1;
}

// Is the song playing?
int I_QrySongPlaying(int handle)
{
   // UNUSED.
   handle = 0;
   return looping || musicdies > gametic;
}

// Interrupt handler.
void I_HandleSoundTimer( int ignore )
{
   (void)ignore;
}

// Get the interrupt. Set duration in millisecs.
int I_SoundSetTimer( int duration_of_tick )
{
   (void)duration_of_tick;
   // Error is -1.
   return 0;
}

// Remove the interrupt. Set duration to zero.
void I_SoundDelTimer(void)
{
}