view pwpzax/zaxplay.c @ 80:d2121ed6e67e

Cosmetics.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 14 Aug 2012 06:17:39 +0300
parents 561fa061caf6
children
line wrap: on
line source

/*
 *  PWP music player. Uses libSDL for audio output.
 *
 *  compile: gcc -O3 -o zaxplay zaxplay.c `sdl-config --libs --cflags`
 *
 *  run: zaxplay <tunenum>
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL.h>
#include <math.h>

typedef struct
{
  char*title;
  int numchans;
  int tempo;

  unsigned char*patord;
  unsigned char*tracks;
} pwpmusic;

#include "Final_Isi.h"
#include "Future_Moottori.h"
#include "Go_Mazzembly.h"
#include "Ikuisuus.h"

static const pwpmusic *tunes[] = {
    &Final_Isi, &Future_Moottori, &Go_Mazzembly, &Ikuisuus
};
static const int ntunes = sizeof(tunes) / sizeof(tunes[0]);
const pwpmusic *tune;


struct {
  struct {
     int wvlgt;
     int wvleft;
     int wvphase;
     int wvlgt1;
  }ch[3];
  int freq;
} pwpgb;

#define pwplib_volume 64
#define pwplib_sound_off(c)       gb_sound(c,0,0,0)
#define pwplib_sound_n(c,n) p     gb_sound(c,n,128,128)
#define pwplib_sound_nv(c,n,v)    gb_sound(c,n,v,128)  
#define pwplib_sound_nvr(c,n,v,r) gb_sound(c,n,v,r)

void gb_sound(int chan,int freq,int volume,int ratio)
{
  if(volume&&freq){
    int wvlgt=
      ((double)(pwpgb.freq*12)/pow(1.059465,freq/256.0));

    if(ratio<1)ratio=1;else if(ratio>255)ratio=255;

    pwpgb.ch[chan].wvlgt=(wvlgt*ratio)>>7;
    pwpgb.ch[chan].wvlgt1=2*pwpgb.ch[chan].wvlgt-wvlgt;

    pwpgb.ch[chan].wvleft=pwpgb.ch[chan].wvlgt/(chan+2);
    pwpgb.ch[chan].wvphase=volume;
  }else{
    pwpgb.ch[chan].wvphase=0;
    pwpgb.ch[chan].wvleft=pwpgb.ch[chan].wvlgt=32123;
  }
}

void gb_init(int freq)
{
  int i;
  pwpgb.freq = freq;
  for(i=0;i<3;i++)
     gb_sound(i,0,0,0);
}

void gb_gen1chan(char*d,int l,int freq)
{
  int ph=128*256;
  while(l--){
    *d++=64+((ph>>8)&0x80);
    ph+=freq;
  }
}

#define ch pwpgb.ch

void gb_beepemu(char*d,int l)
{
  static int pf=0;
  int
    fq0=ch[0].wvlgt,
    fq1=ch[1].wvlgt,
    fq2=ch[2].wvlgt,fq;
  if(ch[0].wvphase==0)fq0=32768;
  if(ch[1].wvphase==0)fq1=32768;
  if(ch[2].wvphase==0)fq2=32768;

  if(fq0>fq1){int tmp=fq0;fq0=fq1;fq1=tmp;}
  if(fq0>fq2){int tmp=fq0;fq0=fq2;fq2=tmp;}
  if(fq1>fq2){int tmp=fq1;fq1=fq2;fq2=tmp;}

  if(pf&1){
    if(pf&2)fq=fq1;else fq=fq2;
    if(!fq)fq=fq0;
  }else{
    fq=fq0;
    if(fq==32768){fq=fq1;
      if(fq==32768)fq=fq2;}
  }
  if(fq==32768)memset(d,128,l);else
  {
    fq=(65536*256)/fq; 
    gb_gen1chan(d,l,fq);
  }
  pf++;
}

void gb_genwave(char*d,int l)
{
   int remain=0;

   l<<=8;
   while(l)
   {
      int min=ch[0].wvleft,w,sum;
          w=ch[1].wvleft;if(w<min)min=w;
          w=ch[2].wvleft;if(w<min)min=w;

      sum=ch[0].wvphase+ch[1].wvphase+ch[2].wvphase+128;
      if(sum<0)sum=0;else if(sum>255)sum=255;

      if(min>l)min=l; l-=min;
      memset(d,sum,(min+remain)>>8);
        d+=((min+remain)>>8);
        remain=(min+remain)&255;

      {int i=0;
      for(;i<3;i++){
         w=ch[i].wvleft-=min;
         if(!w)
         {
           ch[i].wvleft=ch[i].wvlgt1;
           ch[i].wvlgt1=ch[i].wvlgt;
           ch[i].wvlgt=ch[i].wvleft;
           ch[i].wvphase=0-ch[i].wvphase;
         }
       }
      }
    }
}


struct {
   int ord,patt,row;
   int tick,tempo;
   int frag, curr, freq;
} player;


void player_set_tempo(int tempo)
{
   player.tempo = tempo;
   player.frag = (player.freq * tempo) / 73;
}

void player_newnote()
{
   int c=0;
   
   for(;c<tune->numchans;c++)
   {
      int note=tune->tracks[(player.patt * tune->numchans + c) * 64 + player.row];
      if(note==254)
         pwplib_sound_nv(c,0,0);
      else 
      if(note!=255)
         pwplib_sound_nv(c,(note-5)<<8,pwplib_volume);
   }
   player.row++;
   if(player.row==64)
   {
      player.row=0;
      player.ord++;
      player.patt=tune->patord[player.ord];

      if(player.patt>128)
      {
         if(player.patt==255)player.ord=0;else
         {
            if(player.patt==254)player.tempo=4;
            player.ord++;
         }
         player.patt=tune->patord[player.ord];
      }
   }
}

void player_render_cb(void *udata, Uint8 * buf, int len)
{
    (void) udata;
    while (len > 0) {
        if (player.curr >= len) {
            player.curr -= len;
            gb_genwave((char *)buf, len);
            len = 0;
        } else {
            gb_genwave((char *)buf, player.curr);
            buf += player.curr;
            len -= player.curr;
            player.curr = player.frag;
            player_newnote();
        }
    }
}

static void sdlaudio_close(void)
{
    fprintf(stderr, "* Closing audio.\n");
    SDL_CloseAudio();
    SDL_Quit();
}

int main(int argc, char *argv[])
{
   SDL_Event event;
   SDL_AudioSpec fmt;
   int i;

   /* Settings */
   fmt.freq = 44100;
   fmt.format = AUDIO_U8;
   fmt.channels = 1;
   fmt.samples = 2048;
   fmt.callback = player_render_cb;

   if (argc != 2)
   {
      fprintf(stderr, "Usage: %s <tunenum 0..%d>\n",
          argv[0], ntunes - 1);
      return 0;
   }

   i = atoi(argv[1]);
   if (i < 0 || i >= ntunes) i = rand() % ntunes;
   tune = tunes[i];

   fprintf(stderr,
     "Tune #%d: '%s'\n"
     "Tempo: %d\n"
     "Channels: %d\n",
     i, tune->title, tune->tempo, tune->numchans);

   if (SDL_Init(SDL_INIT_AUDIO) != 0)
   {
       fprintf(stderr, "* SDL could not be initialized.\n");
       return 1;
   }

   fprintf(stderr, "* SDL audio request %d, %d, %d -> %d\n",
       fmt.freq, fmt.format, fmt.channels, fmt.samples);

   if (SDL_OpenAudio(&fmt, NULL) < 0)
   {
       fprintf(stderr, "* SDL: Could not get desired audio format.\n");
       return 1;
   }

   fprintf(stderr, "* SDL audio initialized %d, %d, %d -> %d\n",
       fmt.freq, fmt.format, fmt.channels, fmt.samples);
   
   atexit(sdlaudio_close);

   gb_init(fmt.freq);
   player.freq = fmt.freq;
   player.row = player.ord = 0;
   player.patt = tune->patord[0];
   player.tick = 0;
   player_set_tempo(tune->tempo);
   player.curr = player.frag;

   SDL_PauseAudio(0);

   
   while (SDL_WaitEvent(&event) >= 0)
   {
       switch (event.type)
       {
           case SDL_QUIT:
               fprintf(stderr, "* Quitting\n");
               exit(0);
               break;
       }
   }

   sdlaudio_close();   
   return 0;
}