/**
 * @file hearnet.c
 *
 * "Play" your network.
 *
 * @author Hans Fugal
 *
 * heavily modified by Leonard Ritter
 * 
 * Transformed into a SC3 client by Mario Lang
 */

/*
 * To build this file, run
 *  gcc -o schearnet schearnet.c -lpcap -llo -lm
 *
 * In your SUperCollider3 session, evaluate the following piece of code
 * to create the "hernet" synth.  THis is just an example, you can actually
 * change the synth definition at runtime, so experiment a bit!
 *
 * SynthDef("hearnet",{|freq|
 *     Out.ar([0,1],
 *         EnvGen.ar(
 *             Env.perc(Rand(0.001,0.02),Rand(0.2,0.4),curve:'lin'),
 *             doneAction:2
 *         )
 *         *
 *         SinOsc.ar(freq,0,0.05))
 * }).store;
 *
 * Then point schearnet to your SuperCollider3 server, whereever it is running.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pcap.h>
#include <unistd.h>
#include <math.h>
#include <memory.h>
#include <time.h>
#include <lo/lo.h>

lo_address target = NULL;

/** packet handler called by pcap_dispatch in main() */
void packet_handler(u_char * args, const struct pcap_pkthdr *pcap_hdr, const u_char * p)/*{{{*/
{
  float factor2 = (float)(rand()%5 + 1);
  float factor = pow(2,(float)(pcap_hdr->len / 256.0f)*3.0f / 12.0f) * factor2;
  lo_send(target, "/s_new", "siiisf",
	  "hearnet", -1, 1, 0,
	  "freq", 55.0f * factor + ((float)(rand()%5) * factor));
}/*}}}*/

void usage(char *dev)/*{{{*/ 
{
  fprintf(stderr,
	  "\n"
	  "usage: schearnet [-h scsynth-host] [-i interface] [filter-expression]\n"
	  "Default interface is %s.\n", dev);
  exit(1);
}/*}}}*/

/* main */
int main(int argc, char **argv)/*{{{*/
{
  pcap_t *hdl_pcap;
  char perrbuf[PCAP_ERRBUF_SIZE];
  char *dev = NULL;
  char *schost = "127.0.0.1";
  int c;
  char *filter = NULL;

  while ((c = getopt(argc,argv,"h:i:")) != -1) {
    switch (c) {
    case 'h':
      schost = optarg;
      break;
    case 'i':
      dev = optarg;
      break;
    default:
      usage("not determined yet");
      exit(EXIT_FAILURE);
    }
  }
  if (dev == NULL) {
    if ((dev = pcap_lookupdev(perrbuf)) == NULL) {
      perror(perrbuf);
      exit(EXIT_FAILURE);
    }
    printf("Using default interface %s\n", dev);
  }
  target = lo_address_new(schost, "57110");
  if (optind < argc) {
#define MAX_FILTER_SIZE 1024
    filter = malloc(MAX_FILTER_SIZE);
    strncpy(filter,argv[optind++],MAX_FILTER_SIZE);
    while (optind < argc) {
      strncat(filter, " ", MAX_FILTER_SIZE);
      strncat(filter, argv[optind++], MAX_FILTER_SIZE);
    }
#undef MAX_FILTER_SIZE
  }
  /* libpcap stuff */ /*{{{*/
  hdl_pcap = pcap_open_live(dev, BUFSIZ, 0, 0, perrbuf);
  if (hdl_pcap == NULL) {
    fprintf(stderr,"pcap_open_live; %s\n", perrbuf);
    usage(dev);
    exit(EXIT_FAILURE);
  }

  if (filter != NULL) {
    struct bpf_program bpfprog;
    if (pcap_compile(hdl_pcap, &bpfprog, filter, 1, 0XFFFFFFFF) != -1) {
      pcap_setfilter(hdl_pcap, &bpfprog);
      printf("Using filter %s\n", filter);
      pcap_freecode(&bpfprog);
    } else {
      pcap_perror(hdl_pcap, "pcap_compile");
    }
    free(filter);
  }

  printf("Sending to SuperCollider server (scsynth) at %s\n",
	 lo_address_get_url(target));
  puts("Make sure a SynthDef named \"hearnet\" with one argument \"freq\" is loaded.");
  while (1) {
    pcap_dispatch(hdl_pcap, 1, packet_handler, 0);
  }
  /*}}}*/

  return 0;
}/*}}}*/

/* vim:sw=4:ts=8:tw=78:fdm=marker */
