summaryrefslogblamecommitdiffstats
path: root/src/web.c
blob: afd8c57dcdcc54bcb41e82db6d38589ea6a92f4e (plain) (tree)
1
2
3
4
5
6
7

                
                   

                          

                   








                                     
                          







                                                 






                          





                                                                   
 



                                             
 





                                                                    
     
 


                                                          
 



                  































                                                      






































                         




                                                                         
             
















                                                                                    





































                                                                                 





                                                               
                                              






                                                                                  
                                       

                                                                  

                                                                      
                       
          
   

                                                                 
   







                                              
              



                                             

                                                   








                                                   
                                        




                                                       
                                                                                                      











                                                               
                                                         
                 
 

                                    









                                                                                 
                     



                                           



                                                                     


                                               

   
#include "web.h"

#define _GNU_SOURCE
#include <libwebsockets.h>
#include <pthread.h>
#include <unistd.h>

#define HTTP_PORT 9966
#define WS_PORT 9967

static struct lws *client_wsi = NULL;
synth_t * synthx;
struct MHD_Daemon *server;
pthread_t server_thread;
#define BUFFER_SIZE 1024 * 4
char message_buffer[BUFFER_SIZE];
static char *html_content;

// Function to handle slider changes
void handle_slider_change(void *cls, int value) {
  synth_t *synth = (synth_t*)cls;
  printf("Slider changed: %d\n", value);
  cc_set(&synth->cc_cutoff, value);
}

const char *html_header =
  "HTTP/1.1 200 OK\r\n"
  "Connection: close\r\n"
  "Content-Length: %d\r\n"
  "\r\n"
  "\r\n";

char *read_file_to_string(const char *filename) {
    FILE *file = fopen(filename, "rb"); // Open file in binary mode
    if (!file) {
        perror("Error opening file");
        return NULL;
    }

    // Seek to the end to determine file size
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    rewind(file); // Go back to the beginning

    // Allocate memory for the file content (+1 for null terminator)
    char *buffer = (char *)malloc(file_size + 1);
    if (!buffer) {
        perror("Memory allocation failed");
        fclose(file);
        return NULL;
    }

    // Read file into buffer
    fread(buffer, 1, file_size, file);
    buffer[file_size] = '\0'; // Null-terminate the string

    fclose(file);
    return buffer;
}

char *get_from_template() {
  int size = 1024^3 * 1000;
  char * ret = (char *)malloc(sizeof(char) * size);

  int pipefd[2];
  if (pipe(pipefd) == -1) {
    perror("pipe");
    return NULL;
  }

  synth_t * synth = synthx;
  #define OUT pipefd[1]
  #define INT(x) dprintf(OUT, "%d", x);
  #define FLOAT(x) dprintf(OUT, "%f", x);
  #define STR(x) dprintf(OUT, "%s", x);
  #define PERCENT dprintf(OUT, "%s", "%");
  #include "index.html.h"
  close(pipefd[1]);

  // Read from the pipe into a buffer
  ssize_t bytes_read = read(pipefd[0], ret, size - 1);
  if (bytes_read == -1) {
    perror("read");
    return NULL;
  }

  // Null-terminate the ret
  ret[bytes_read] = '\0';
  close(pipefd[0]);
  return ret;
}

int
key_to_number(char key) {
  switch (key) {
  case 'q':
    return 0;
  case '2':
    return 1;
  case 'w':
    return 2;
  case '3':
    return 3;
  case 'e':
    return 4;
  case 'r':
    return 5;
  case '5':
    return 6;
  case 't':
    return 7;
  case '6':
    return 8;
  case 'y':
    return 9;
  case '7':
    return 10;
  case 'u':
    return 11;
  case 'i':
    return 12;
  case '9':
    return 13;
  case 'o':
    return 14;
  case '0':
    return 15;
  case 'p':
    return 16;
  }
  return 0;
}

// Callback to handle WebSocket events
static int callback_ws(struct lws *wsi, enum lws_callback_reasons reason,
                       void *user, void *in, size_t len) {
  (void)user;
  char buf[10000] = "";
  char tmp[10000] = "";
  switch (reason) {
  case LWS_CALLBACK_ESTABLISHED: { // When a connection is established
    client_wsi = wsi; // Store the WebSocket connection for later use
    printf("WebSocket connection established with client: %p\n", wsi);
    /* const char *msg = "Hello, Client!"; */
    /* printf("Sending message: %s\n", msg); */
    /* int n = lws_write(wsi, (unsigned char *)msg, strlen(msg), LWS_WRITE_TEXT); */
    /* if (n < 0) { */
    /*     printf("Error sending message, error code: %d\n", n); */
    /* } */
    break;
  }
  case LWS_CALLBACK_RECEIVE: { // When a message is received
    char buffer[128];
    snprintf(buffer, sizeof(buffer), "%.*s", (int)len, (char *)in);
    printf("Got ws message: [%s]\n", buffer);
    if (!strcmp("note_on", buffer)) {
      break;
    }
    if (!strcmp("note_off", buffer)) {
      break;
    }
    if (buffer[0] == '+') {
      char key = buffer[1];
      int i = key_to_number(key);
      synthx->midi_note[i].freq = 16.35160 * pow(2, (synthx->octave + i / 12.0));
      synthx->midi_note[i].channel = -1;
      synthx->midi_note[i].noteOn = Pa_GetStreamTime(synthx->stream);
      synthx->midi_note[i].noteOff = 0;
      synthx->midi_note[i].velocity = 1.0;
      synthx->midi_note[i].elapsed = 0;
      synthx->midi_note[i].active = 1;
      int flag = 1;
      for (int j = 0; j < synthx->midi_active_n; j++) {
        if (synthx->midi_active[j] == &synthx->midi_note[i]) {
          flag = 0;
        }
      }
      if (flag) {
        synthx->midi_active[synthx->midi_active_n++] = &synthx->midi_note[i];
      }

      //synth->adsr.elapsed = 0;
      synthx->active = 1;
      break;
    }
    if (buffer[0] == '-') {
      char key = buffer[1];
      int i = key_to_number(key);
      synthx->midi_note[i].noteOff = Pa_GetStreamTime(synthx->stream);
      synthx->midi_note[i].noteOffSample = synthx->midi_note[i].elapsed;
      break;
    }
    //lws_write(wsi, (unsigned char *)in, len, LWS_WRITE_TEXT);
    int value = atoi(buffer);
    handle_slider_change(synthx, value);
    break;
  }
  case LWS_CALLBACK_SERVER_WRITEABLE: {
    printf("LWS_CALLBACK_SERVER_WRITEABLE\n");
    /* size_t msg_len = strlen(message_buffer); */
    /* unsigned char buffer[LWS_PRE + BUFFER_SIZE]; */
    /* memcpy(&buffer[LWS_PRE], message_buffer, msg_len); */
    /* lws_write(wsi, (unsigned char *)buffer, strlen(buffer), LWS_WRITE_TEXT); */
    break;
  }
  case LWS_CALLBACK_HTTP: {
    html_content = get_from_template();
    snprintf(tmp, sizeof(tmp), html_header, strlen(html_content));
    strcpy(buf, tmp);
    strcat(buf, html_content);
    lws_write(wsi, (unsigned char *)buf, strlen(buf), LWS_WRITE_HTTP);
    free(html_content);
    break;
  }
  case LWS_CALLBACK_CLOSED: {
    printf("WebSocket connection closed with client: %p\n", wsi);
  }
  default:
    break;
  }
  return 0;
}

// Thread function to run the WebSocket server
void *websocket_server_thread(void *arg) {
    (void)arg;
    struct lws_context_creation_info info;
    struct lws_context *context;
    struct lws_protocols protocols[] = {
      //{ "http-only", callback_http, 0, 0 },
        { "ws", callback_ws, 0, 128, 0, 0, 0 },
        { NULL, NULL, 0, 0, 0, 0, 0 } // Terminator
    };

    memset(&info, 0, sizeof(info));
    info.port = WS_PORT;
    //info.user = arg;
    info.protocols = protocols;
    info.pt_serv_buf_size = 32 * 1024;
    info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;

    context = lws_create_context(&info);
    if (!context) {
        fprintf(stderr, "lws_create_context failed\n");
        return NULL;
    }

    printf("WebSocket server running on ws://localhost:%d [http://localhost:%d]\n", WS_PORT, WS_PORT);

    while (1) {
        lws_service(context, 1000); // Service WebSocket events
    }

    lws_context_destroy(context);
    return NULL;
}

void
init_web(synth_t * synth)
{
  //html_content = read_file_to_string("src/index.html");
  synthx = synth;

  lws_set_log_level(LLL_WARN, NULL);

  // Create a new thread for the WebSocket server
  if (pthread_create(&server_thread, NULL, websocket_server_thread, NULL) != 0) {
    fprintf(stderr, "Failed to create server thread\n");
    return;
  }
}

void
free_web()
{
  free(html_content);
}

void ws_send_message(const char *message) {
  if (client_wsi != NULL) {
    size_t msg_len = strlen(message);
    unsigned char buffer[LWS_PRE + BUFFER_SIZE];
    memcpy(&buffer[LWS_PRE], message, msg_len);
    lws_write(client_wsi, &buffer[LWS_PRE], msg_len, LWS_WRITE_TEXT);
    // printf("[WS]: Sent <<%s>>\n", message);
    //   do I need this?
    //lws_callback_on_writable(client_wsi); ???
  }
}