Here is the full code for wave generation!

A few things to consider:

  1. This code is designed with Linux in mind and can only run in a Linux environment. If you wish to run it on Windows or Mac, you'll have to replace the file generation functions with those that your system supports.
  2. PGM images take up space!!! First and foremost, make a new folder called ./frames. Then, consider mounting a tmpfs file system so it resides in RAM.


#include <stdio.h>

#define ROW 128 
#define COL 128 
#define SIZE ROW*COL
#define EXP 12
#define ONE 0x1000 //one with 12 binary places
#define FRAMES 200 //we're gonna do 200 frames

int mulf(int a, int b) {
  int res = a * b;
  //for a right bit shift, you have to handle the cases where result is negative
  if (res < 0) return -(-res >> EXP);
  return res >> EXP;
}

const int c2 = 0x400; //This is 1/4 in this fixed point system, so c^2 = 0.25
void compute_next(int *next, int *curr, int *prev) {
  for (int x=ROW; x<SIZE-ROW; x+=ROW) {
    for (int y=1; y<ROW-1; y++) {
      int index = x+y; // no need to recompute x+y everywhere!
      int dt = (curr[index]<<1) - prev[index];
      int ds = (curr[index]<<2) - curr[index+1] - curr[index-1] - curr[index+ROW] - curr[index-ROW];
      next[index] = dt - mulf(ds, c2);
    }
  }
  //edge along y=0
  for (int x=0; x<SIZE; x+=ROW) next[x] = 0;
  //edge along y=ROW-1
  for (int x=ROW-1; x<SIZE; x+=ROW) next[x] = 0;
  //edge along x=0
  for (int y=0; y<ROW; y++) next[y] = 0;
  //edge along x=SIZE-ROW
  for (int y=SIZE-ROW; y<SIZE; y++) next[y] = 0;
}

void generate_pgm(int *array, int *pgmidx) {
  char filename[64];
  sprintf(filename, "./frames/frame%03d.pgm", *pgmidx);
  FILE *fd = fopen(filename, "wb+");
  fprintf(fd, "P5\n%d %d\n255\n", ROW, COL);
  for (int i=0; i<SIZE; i++) {
    int temp = 0x7f + mulf(0x7f,array[i]); //Normalize with zero being middle grey
    if(temp > 0xff) temp = 0xff;
    else if(temp < 0) temp = 0;
    fputc(temp, fd);
  }
  *pgmidx = *pgmidx + 1;
  fclose(fd);
}

int main(int argc, char **argv) {
  int buf0[SIZE];
  int buf1[SIZE];
  int buf2[SIZE];

  //Now have pointers point to these buffers, so it's possible to switch between them
  int *next = buf0;
  int *curr = buf1;
  int *prev = buf2;

  //initialise arrays
  for (int i=0; i<SIZE; i++) next[i] = curr[i] = prev[i] = 0;

  //Trigger one wave in the middle
  int MIDX = ROW>>1;
  int MIDY = COL>>1;
  curr[MIDX+MIDY*ROW] = ONE<<2; //Let's trigger a wave with amplitude 4

  int pgmidx = 0; //frame name index
  int framecount = 0; //This is used to render only every 8th frame!
  for (int i=0; i<FRAMES<<3; i++) { // important to multiply FRAME with 8
    compute_next(next, curr, prev);
    framecount++;
    if(framecount&0b1000) {
      framecount = 0;
      generate_pgm(next, &pgmidx);
    }
    // swap the buffers!
    int *ptr = prev;
    prev = curr;
    curr = next;
    next = ptr;
  }
  return 0;
}