[xiph-cvs] cvs commit: MTG soundboard.c
Monty
xiphmont at xiph.org
Wed Jan 30 04:28:37 PST 2002
xiphmont 02/01/30 04:28:36
Added: . soundboard.c
Log:
'twould be bad to lose it.
Monty
Revision Changes Path
1.1 MTG/soundboard.c
Index: soundboard.c
===================================================================
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <pthread.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#include <curses.h>
<p>#define int16 short
static char *tempdir="/tmp/beaverphonic/";
static char *installdir="/usr/local/beaverphonic/";
#define VERSION "20020128.0"
enum menutype {MENU_MAIN,MENU_KEYPRESS,MENU_ADD,MENU_EDIT,MENU_OUTPUT,MENU_QUIT};
pthread_mutex_t master_mutex=PTHREAD_MUTEX_INITIALIZER;
static int main_master_volume=50;
char *program;
static int playback_buffer_minfill=-1;
static int running=1;
static enum menutype menu=MENU_MAIN;
static int cue_list_position=0;
static int cue_list_number=0;
static int firstsave=0;
static int unsaved=0;
void *m_realloc(void *in,int bytes){
if(!in)
return(malloc(bytes));
return(realloc(in,bytes));
}
void addnlstr(char *s,int n,char c){
int len=strlen(s),i;
addnstr(s,n);
n-=len;
for(i=0;i<n;i++)addch(c);
}
<p>/******** channel mappings. All hardwired for now... ***********/
// only OSS stereo builin for now
#define MAX_CHANNELS 2
#define CHANNEL_LABEL_LENGTH 50
typedef struct {
char label[CHANNEL_LABEL_LENGTH];
int peak;
/* real stuff not here yet */
} outchannel;
static outchannel channel_list[MAX_CHANNELS]={
{"offstage left",0},
{"offstage right",0},
};
static int channel_count=MAX_CHANNELS;
/******** label abstraction code; use this for all alloced strings
that need to be saved to config file (shared or not) */
typedef struct {
char *text;
int refcount;
} label;
typedef int label_number;
tatic label *label_list;
static int label_count;
int new_label_number(){
int i;
for(i=0;i<label_count;i++)
if(!label_list[i].refcount)break;
return(i);
}
tatic void _alloc_label_if_needed(int number){
if(number>=label_count){
int prev=label_count;
label_count=number+1;
label_list=m_realloc(label_list,sizeof(*label_list)*label_count);
memset(label_list+prev,0,sizeof(*label_list)*(label_count-prev));
}
}
void edit_label(int number,char *t){
label *label;
_alloc_label_if_needed(number);
label=label_list+number;
label->text=m_realloc(label->text,strlen(t)+1);
strcpy(label->text,t);
}
void aquire_label(int number){
_alloc_label_if_needed(number);
label_list[number].refcount++;
}
void release_label(int number){
label *label;
if(number>=label_count)return;
label=label_list+number;
label->refcount--;
if(label->refcount<0)
label->refcount=0;
}
int save_label(FILE *f,int number){
int count;
if(number<=label_count){
if(label_list[number].refcount){
fprintf(f,"LBL:%d %d:%s",number,
strlen(label_list[number].text),
label_list[number].text);
count=fprintf(f,"\n");
if(count<1)return(-1);
}
}
return(0);
}
int save_labels(FILE *f){
int i;
for(i=label_count-1;i>=0;i--)
if(save_label(f,i))return(-1);
return(0);
}
int load_label(FILE *f){
int len,count,number;
char *temp;
count=fscanf(f,":%d %d:",&number,&len);
if(count<2){
addnlstr("LOAD ERROR (LBL): too few fields.",80,' ');
return(-1);
}
temp=alloca(len+1);
count=fread(temp,1,len,f);
temp[len]='\0';
if(count<len){
addnlstr("LOAD ERROR (LBL): EOF reading string.",80,' ');
return(-1);
}
edit_label(number,temp);
return(0);
}
/************* tag abstraction **********************/
typedef int tag_number;
typedef struct {
int refcount;
label_number sample_path;
int loop_p;
long loop_start;
long loop_lapping;
long fade_out;
void *basemap;
int16 *channel_vec[2];
int channels;
long samplelength;
/* state */
int activep;
long sample_position;
long sample_lapping;
double master_vol_current;
double master_vol_target;
double master_vol_slew;
double outvol_current[2][MAX_CHANNELS];
double outvol_target[2][MAX_CHANNELS];
double outvol_slew[2][MAX_CHANNELS];
} tag;
truct sample_header{
long sync;
long channels;
long samples;
};
tatic tag *tag_list;
static int tag_count;
tatic tag **active_list;
static int active_count;
int new_tag_number(){
int i;
for(i=0;i<tag_count;i++)
if(!tag_list[i].refcount)break;
return(i);
}
tatic void _alloc_tag_if_needed(int number){
if(number>=tag_count){
int prev=tag_count;
tag_count=number+1;
tag_list=m_realloc(tag_list,sizeof(*tag_list)*tag_count);
active_list=m_realloc(active_list,sizeof(*active_list)*tag_count);
memset(tag_list+prev,0,sizeof(*tag_list)*(tag_count-prev));
}
}
/* UI convention: this has too many useful things to report if sample
opening goes awry, and I don't want to define a huge message
reporting system through ncurses in C, so we go back to tty-like
for reporting here, waiting for a keypress if nonzero return */
int edit_tag(int number,tag t){
int offset,ret;
tag *tag;
_alloc_tag_if_needed(number);
t.basemap=NULL;
t.channels=0;
t.samplelength=0;
tag_list[number]=t;
tag=tag_list+number;
/* allow the edit to be accepted if the file cannot be opened, but
alert the operator */
{
/* parse the file; use preexisting tools via external Perl glue */
/* select a temp file for the glue to convert file into */
/* valid use of mktemp; only one Beaverphonic is running a time,
and we need a *name*, not a *file*. */
char *template=alloca(strlen(tempdir)+20);
char *tmp;
sprintf(template,"%sconversionXXXXXX",tempdir);
tmp=mktemp(template);
if(!tmp)return(-1);
{
char *orig=label_list[t.sample_path].text;
char *buffer=alloca(strlen(installdir)+strlen(tmp)+strlen(orig)+20);
sprintf(buffer,"%sconvert.pl %s %s",installdir,orig,tmp);
ret=system(buffer);
if(ret)return(ret);
}
/* our temp file is a host-ordered 44.1kHz 16 bit PCM file, n
channels uninterleaved. first word is channels */
{
FILE *cheat=fopen(tmp,"rb");
struct sample_header head;
int count,i;
int fd;
if(!cheat){
fprintf(stderr,"Failed to open converted file %s:\n\t%d (%s)\n",
tmp,ferror(cheat),strerror(ferror(cheat)));
return(-1);
}
count=fread(&head,sizeof(head),1,cheat);
fclose(cheat);
if(count<(int)sizeof(head)){
fprintf(stderr,"Conversion file %s openable, but truncated\n",tmp);
return(-1);
}
if(head.sync!=55){
fprintf(stderr,"Conversion file created by incorrect "
"version of convert.pl\n\t%s unreadable\n",tmp);
return(-1);
}
tag->channels=head.channels;
tag->samplelength=head.samples;
offset=sizeof(head);
/* mmap the sample file */
fd=open(tmp,O_RDONLY);
if(fd<0){
fprintf(stderr,"Unable to open %s fd for mmap:\n\t%d (%s)\n",
tmp,errno,strerror(errno));
return(-1);
}
tag->basemap=
mmap(NULL,
tag->samplelength*sizeof(int16)*tag->channels+sizeof(head),
PROT_READ,MAP_PRIVATE,fd,0);
close(fd);
if(!tag->basemap){
fprintf(stderr,"Unable to mmap fd %d (%s):\n\t%d (%s)\n",
fd,tmp,errno,strerror(errno));
return(-1);
}
for(i=0;i<tag->channels && i<2 ;i++) /* only up to stereo channels */
tag->channel_vec[i]=(int16 *)(tag->basemap)+
sizeof(head)+head.samples*i;
}
}
/* state is zeroed when we go to production mode. Done */
return(0);
}
void aquire_tag(int number){
if(number<0)return;
_alloc_tag_if_needed(number);
tag_list[number].refcount++;
}
void release_tag(int number){
if(number<0)return;
if(number>=tag_count)return;
tag_list[number].refcount--;
if(tag_list[number].refcount<0)
tag_list[number].refcount=0;
}
int save_tag(FILE *f,int number){
tag *t;
int count;
if(number>=tag_count)return(0);
t=tag_list+number;
if(t->refcount){
fprintf(f,"TAG:%d %d %ld %ld %ld",
number,t->sample_path,t->loop_p,t->loop_start,t->loop_lapping,
t->fade_out);
count=fprintf(f,"\n");
if(count<1)return(-1);
}
return(0);
}
int save_tags(FILE *f){
int i;
for(i=tag_count-1;i>=0;i--){
if(save_tag(f,i))return(-1);
}
return(0);
}
int load_tag(FILE *f){
tag t;
int len,count,number;
memset(&t,0,sizeof(t));
count=fscanf(f,":%d %d %ld %ld %ld",
&number,&t.sample_path,&t.loop_p,&t.loop_start,
&t.loop_lapping,&t.fade_out);
if(count<5){
addnlstr("LOAD ERROR (TAG): too few fields.",80,' ');
return(-1);
}
aquire_label(t.sample_path);
edit_tag(number,t);
return(0);
}
/********************* cue abstraction ********************/
typedef struct {
int vol_master;
long vol_ms;
int outvol[2][MAX_CHANNELS];
} mix;
typedef struct {
label_number label;
label_number cue_text;
label_number cue_desc;
tag_number tag;
int tag_create_p;
mix mix;
} cue;
tatic cue *cue_list;
int cue_count;
int cue_storage;
void add_cue(int n,cue c){
int i;
if(cue_count==cue_storage){
cue_storage++;
cue_storage*=2;
cue_list=m_realloc(cue_list,cue_storage*sizeof(*cue_list));
}
/* copy insert */
if(cue_count-n)
memmove(cue_list+n+1,cue_list+n,sizeof(*cue_list)*(cue_count-n));
cue_count++;
cue_list[n]=c;
}
/* inefficient. delete one, shift-copy list, delete one, shift-copy
list, delete one, gaaaah. Not worth optimizing */
static void _delete_cue_helper(int n){
if(n>=0 && n<cue_count){
release_label(cue_list[n].label);
release_label(cue_list[n].cue_text);
release_label(cue_list[n].cue_desc);
release_tag(cue_list[n].tag);
cue_count--;
if(n<cue_count)
memmove(cue_list+n,cue_list+n+1,sizeof(*cue_list)*(cue_count-n));
}
}
/* slightly more complicated that just removing the cue from memory;
we need to remove any tag mixer modification cues that follow if
this cue creates a sample tag */
void delete_cue_single(int n){
int i;
if(n>=0 && n<cue_count){
cue *c=cue_list+n;
tag_number tag=c->tag;
if(c->tag_create_p){
/* tag creation cue; have to delete following cues matching this
tag */
for(i=cue_count-1;i>n;i--)
if(cue_list[i].tag==tag)
_delete_cue_helper(i);
}
_delete_cue_helper(n);
}
}
/* this deletes all cues of a cue bank, and also chases the tags of
sample creation cues */
void delete_cue_bank(int n){
/* find first cue number */
int first=n,last=n;
while(first>0 && cue_list[first].label==cue_list[first-1].label)first--;
while(last+1<cue_count &&
cue_list[last].label==cue_list[last+1].label)last++;
for(;last>=first;last--)
delete_cue_single(last);
}
int save_cue(FILE *f,int n){
if(n<cue_count){
int count,i;
cue *c=cue_list+n;
fprintf(f,"CUE:%d %d %d %d %d %d %ld :%d:%d:",
c->label,c->cue_text,c->cue_desc,
c->tag,c->tag_create_p,
c->mix.vol_master,
c->mix.vol_ms,
MAX_CHANNELS,
2);
for(i=0;i<MAX_CHANNELS;i++)
fprintf(f,"<%d %d>",c->mix.outvol[0][i],c->mix.outvol[1][i]);
count=fprintf(f,"\n");
if(count<1)return(-1);
}
return(0);
}
int save_cues(FILE *f){
int i;
for(i=0;i<cue_count;i++)
if(save_cue(f,i))return(-1);
return(0);
}
int load_cue(FILE *f){
cue c;
int len,count,i,j;
int maxchannels,maxwavch;
memset(&c,0,sizeof(c));
count=fscanf(f,":%d %d %d %d %d %d %ld :%d:%d:",
&c.label,&c.cue_text,&c.cue_desc,
&c.tag,&c.tag_create_p,
&c.mix.vol_master,
&c.mix.vol_ms,
&maxchannels,&maxwavch);
if(count<9){
addnlstr("LOAD ERROR (CUE): too few fields.",80,' ');
return(-1);
}
for(i=0;i<maxchannels;i++){
int v;
long lv;
char ch=' ';
count=fscanf(f,"%c",&ch);
if(count<0 || ch!='<'){
addnlstr("LOAD ERROR (CUE): parse error looking for '<'.",80,' ');
return(-1);
}
for(j=0;j<maxwavch;j++){
count=fscanf(f,"%d",&v);
if(count<1){
addnlstr("LOAD ERROR (CUE): parse error looking for value.",80,' ');
return(-1);
}
if(j<2)
c.mix.outvol[j][i]=v;
}
count=fscanf(f,"%c",&ch);
if(count<0 || ch!='>'){
addnlstr("LOAD ERROR (CUE): parse error looking for '>'.",80,' ');
return(-1);
}
}
aquire_label(c.label);
aquire_label(c.cue_text);
aquire_label(c.cue_desc);
aquire_tag(c.tag);
add_cue(cue_count,c);
return(0);
}
int load_val(FILE *f, int *val){
int count;
int t;
count=fscanf(f,": %d",&t);
if(count<1){
addnlstr("LOAD ERROR (VAL): missing field.",80,' ');
return(-1);
}
*val=t;
return(0);
}
int save_val(FILE *f, char *p, int val){
int count;
fprintf(f,"%s: %d",p,val);
count=fprintf(f,"\n");
if(count<1)return(-1);
return(0);
}
int load_program(FILE *f){
char buf[3];
int ret=0,count;
move(0,0);
attron(A_BOLD);
while(1){
if(fread(buf,1,3,f)<3){
addnlstr("LOAD ERROR: truncated program.",80,' ');
ret=-1;
break;
}
if(!strncmp(buf,"TAG",3)){
ret|=load_tag(f);
}else if(!strncmp(buf,"CUE",3)){
ret|=load_cue(f);
}else if(!strncmp(buf,"LBL",3)){
ret|=load_label(f);
}else if(!strncmp(buf,"MAS",3)){
ret|=load_val(f,&main_master_volume);
}
while(1){
count=fgetc(f);
if(count=='\n' || count==EOF)break;
}
if(count==EOF)break;
while(1){
count=fgetc(f);
if(count!='\n' || count==EOF)break;
}
if(count!=EOF)
ungetc(count,f);
if(count==EOF)break;
}
attroff(A_BOLD);
if(ret)getch();
return(ret);
}
int save_program(FILE *f){
int ret=0;
ret|=save_labels(f);
ret|=save_tags(f);
ret|=save_cues(f);
ret|=save_val(f,"MAS",main_master_volume);
return ret;
}
/***************** simple form entry fields *******************/
void switch_to_stderr(){
def_prog_mode(); /* save current tty modes */
endwin(); /* restore original tty modes */
}
void switch_to_ncurses(){
refresh(); /* restore save modes, repaint screen */
}
enum field_type { FORM_YESNO, FORM_PERCENTAGE, FORM_NUMBER, FORM_GENERIC,
FORM_BUTTON } ;
typedef struct {
enum field_type type;
int x;
int y;
int width;
void *var;
int active;
int cursor;
} formfield;
typedef struct {
formfield *fields;
int count;
int storage;
int edit;
int cursor;
} form;
void form_init(form *f,int maxstorage){
memset(f,0,sizeof(*f));
f->fields=calloc(maxstorage,sizeof(formfield));
f->storage=maxstorage;
}
void form_clear(form *f){
if(f->fields)free(f->fields);
memset(f,0,sizeof(*f));
}
void draw_field(formfield *f,int edit,int focus){
int y,x;
int i;
getyx(stdscr,y,x);
move(f->y,f->x);
if(f->type==FORM_BUTTON)edit=0;
if(edit && f->active)
attron(A_REVERSE);
addch('[');
if(f->active){
if(edit){
attrset(0);
}else{
if(focus){
attron(A_REVERSE);
}else{
attron(A_BOLD);
}
}
switch(f->type){
case FORM_YESNO:
{
int *var=(int *)(f->var);
char *s="No ";
if(*var)
s="Yes";
for(i=0;i<f->width-5;i++)
addch(' ');
addstr(s);
}
break;
case FORM_PERCENTAGE:
{
int var=*(int *)(f->var);
char buf[80];
if(var<0)var=0;
if(var>100)var=100;
snprintf(buf,80,"%*d",f->width-2,var);
addstr(buf);
}
break;
case FORM_NUMBER:
{
int var=*(int *)(f->var);
char buf[80];
snprintf(buf,80,"%*d",f->width-2,var);
addstr(buf);
}
break;
case FORM_GENERIC:case FORM_BUTTON:
{
char *var=(char *)(f->var);
addnlstr(var,f->width-2,' ');
}
break;
}
attrset(0);
}else{
attrset(0);
addnlstr("",f->width-2,'-');
}
if(edit &&
f->active)
attron(A_REVERSE);
addch(']');
attrset(0);
/* cursor? */
move(y,x);
if(focus && edit && f->type==FORM_GENERIC){
curs_set(1);
move(f->y,f->x+1+f->cursor);
}else{
curs_set(0);
}
}
void form_redraw(form *f){
int i;
for(i=0;i<f->count;i++)
draw_field(f->fields+i,f->edit,i==f->cursor);
}
int field_add(form *f,enum field_type type,int x,int y,int width,void *var){
int n=f->count;
if(f->storage==n)return(-1);
/* add the struct, then draw contents */
f->fields[n].type=type;
f->fields[n].x=x;
f->fields[n].y=y;
f->fields[n].width=width;
f->fields[n].var=var;
f->fields[n].active=1;
f->count++;
draw_field(f->fields+n,f->edit,n==f->cursor);
return(n);
}
void field_state(form *f,int n,int activep){
if(n<f->count){
f->fields[n].active=activep;
draw_field(f->fields+n,f->edit,n==f->cursor);
}
}
void form_next_field(form *f){
formfield *ff=f->fields+f->cursor;
draw_field(f->fields+f->cursor,0,0);
while(1){
f->cursor++;
if(f->cursor>=f->count)f->cursor=0;
ff=f->fields+f->cursor;
if(ff->active)break;
}
draw_field(f->fields+f->cursor,f->edit,1);
}
void form_prev_field(form *f){
formfield *ff=f->fields+f->cursor;
draw_field(f->fields+f->cursor,0,0);
while(1){
f->cursor--;
if(f->cursor<0)f->cursor=f->count-1;
ff=f->fields+f->cursor;
if(ff->active)break;
}
draw_field(f->fields+f->cursor,f->edit,1);
}
/* returns >=0 if it does not handle the character */
int form_handle_char(form *f,int c){
formfield *ff=f->fields+f->cursor;
int ret=-1;
switch(c){
case KEY_ENTER:
case '\n':
case '\r':
if(ff->type==FORM_BUTTON){
f->edit=0;
ret=KEY_ENTER;
}else{
if(f->edit){
f->edit=0;
//draw_field(f->fields+f->cursor,f->edit,1);
//form_next_field(f);
}else{
f->edit=1;
}
}
break;
case KEY_UP:
form_prev_field(f);
break;
case KEY_DOWN:case '\t':
form_next_field(f);
break;
default:
if(f->edit){
switch(ff->type){
case FORM_YESNO:
{
int *val=(int *)ff->var;
switch(c){
case 'y':case 'Y':
*val=1;
break;
case 'n':case 'N':
*val=0;
break;
case ' ':
if(*val)
*val=0;
else
*val=1;
break;
default:
ret=c;
break;
}
}
break;
case FORM_PERCENTAGE:
{
int *val=(int *)ff->var;
switch(c){
case '+':case '=':case KEY_LEFT:
(*val)++;
if(*val>100)*val=100;
break;
case '-':case '_':case KEY_RIGHT:
(*val)--;
if(*val<0)*val=0;
break;
default:
ret=c;
break;
}
}
break;
case FORM_NUMBER:
{
int *val=(int *)ff->var;
switch(c){
case '0':case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
if(*val<(int)rint(pow(10,ff->width-3))){
(*val)*=10;
(*val)+=c-48;
}
break;
case KEY_BACKSPACE:case '\b':
(*val)/=10;
break;
case KEY_RIGHT:case '+':case '=':
if(*val<(int)rint(pow(10,ff->width-2)-1))
(*val)++;
break;
case KEY_LEFT:case '-':case '_':
if(*val>0)
(*val)--;
break;
default:
ret=c;
break;
}
}
break;
/* we assume the string for the GENERIC case is alloced to width */
case FORM_GENERIC:
{
char *val=(char *)ff->var;
const char *ctrl=unctrl(c);
switch(c){
case KEY_LEFT:
ff->cursor--;
if(ff->cursor<0)ff->cursor=0;
break;
case KEY_RIGHT:
ff->cursor++;
if(ff->cursor>(int)strlen(val))ff->cursor=strlen(val);
if(ff->cursor>ff->width-3)ff->cursor=ff->width-3;
break;
case KEY_BACKSPACE:case '\b':
if(ff->cursor>0){
memmove(val+ff->cursor-1,val+ff->cursor,strlen(val)-ff->cursor+1);
ff->cursor--;
}
break;
default:
if(isprint(c)){
if((int)strlen(val)<ff->width-3){
memmove(val+ff->cursor+1,val+ff->cursor,strlen(val)-ff->cursor+1);
val[ff->cursor]=c;
ff->cursor++;
}
}else{
if(ctrl[0]=='^'){
switch(ctrl[1]){
case 'A':case 'a':
ff->cursor=0;
break;
case 'E':case 'e':
ff->cursor=strlen(val);
break;
case 'K':case 'k':
val[ff->cursor]='\0';
break;
default:
ret=c;
break;
}
}else{
ret=c;
}
}
break;
}
}
break;
default:
ret=c;
break;
}
}else{
ret=c;
}
}
draw_field(f->fields+f->cursor,f->edit,1);
return(ret);
}
/********************** main run screen ***********************/
void main_update_master(int n,int y){
if(menu==MENU_MAIN){
char buf[4];
if(n>100)n=100;
if(n<0)n=0;
pthread_mutex_lock(&master_mutex);
main_master_volume=n;
pthread_mutex_unlock(&master_mutex);
move(y,8);
addstr("master: ");
sprintf(buf,"%3d%%",main_master_volume);
addstr(buf);
}
}
void main_update_playbuffer(int y){
if(menu==MENU_MAIN){
char buf[20];
int n;
static int starve=0;
pthread_mutex_lock(&master_mutex);
n=playback_buffer_minfill;
pthread_mutex_unlock(&master_mutex);
if(n==0)starve=1;
if(n<0){
starve=0; /* reset */
n=0;
}
move(y,4);
addstr("playbuffer: ");
sprintf(buf,"%3d%% %s",n,starve?"***STARVE***":" ");
addstr(buf);
}
}
#define todB_nn(x) ((x)==0.f?-400.f:log((x))*8.6858896f)
void main_update_outchannel_levels(int y){
int i,j;
if(menu==MENU_MAIN){
for(i=0;i<MAX_CHANNELS;i++){
int val;
char buf[11];
pthread_mutex_lock(&master_mutex);
val=channel_list[i].peak;
pthread_mutex_unlock(&master_mutex);
move(y+i+1,24);
if(val>=32767){
attron(A_BOLD);
addstr("CLIP");
}else{
addstr("+0dB");
}
attron(A_BOLD);
move(y+i+1,13);
val=rint(10.+(todB_nn(val/32768.)/10));
for(j=0;j<val;j++)buf[j]='=';
buf[j]='|';
for(;j<10;j++)buf[j]=' ';
buf[j]='\0';
addstr(buf);
attroff(A_BOLD);
}
}
}
void main_update_outchannel_labels(int y){
int i;
char buf[80];
if(menu==MENU_MAIN){
for(i=0;i<MAX_CHANNELS;i++){
move(y+i+1,4);
sprintf(buf,"%2d: -Inf[ ]+0dB ",i);
addstr(buf);
pthread_mutex_lock(&master_mutex);
addstr(channel_list[i].label);
pthread_mutex_unlock(&master_mutex);
}
}
main_update_outchannel_levels(y);
}
void main_update_cues(int y){
if(menu==MENU_MAIN){
int cn=cue_list_number-1,i;
for(i=-1;i<2;i++){
char buf[80];
move(y+i*2+2,0);
if(i==0){
addstr("NEXT => ");
attron(A_REVERSE);
}
if(cn<0){
move(y+i*2+2,8);
addnlstr("",71,' ');
move(y+i*2+3,8);
addnlstr("**** BEGIN",71,' ');
}else if(cn>=cue_count){
move(y+i*2+2,8);
addnlstr("****** END",71,' ');
attroff(A_REVERSE);
move(y+i*2+3,8);
addnlstr("",71,' ');
if(i==0){
move(y+i*2+4,8);
addnlstr("",71,' ');
move(y+i*2+5,8);
addnlstr("",71,' ');
}
break;
}else{
cue *c;
c=cue_list+cn;
snprintf(buf,13,"%10s) ",label_list[c->label].text);
mvaddstr(y+i*2+2,8,buf);
addnlstr(label_list[c->cue_text].text,59,' ');
mvaddstr(y+i*2+3,8," ");
addnlstr(label_list[c->cue_desc].text,59,' ');
}
attroff(A_BOLD);
while(++cn<cue_count)
if(cue_list[cn].tag==-1)break;
attroff(A_REVERSE);
}
}
}
/* assumes the existing tags/labels are not changing out from
underneath playback. editing a tag *must* kill playback for
stability */
void main_update_tags(int y){
if(menu==MENU_MAIN){
int i;
move(y,0);
if(active_count){
int len;
addstr("playing tags:");
for(i=0;i<active_count;i++){
int loop;
int ms;
char buf[8];
label_number path;
move(y+i,14);
/* normally called by playback so no locking */
loop=active_list[i]->loop_p;
ms=active_list[i]->samplelength-active_list[i]->sample_position;
path=active_list[i]->sample_path;
if(loop)
sprintf(buf,"[loop] ");
else
sprintf(buf,"[%3ds] ",(ms+500)/1000);
addstr(buf);
addnlstr(label_list[path].text,55,' ');
}
move(y+i,14);
addnlstr("",55,' ');
}else{
addstr(" ");
move(y,14);
addnlstr("",55,' ');
}
}
}
tatic int editable=1;
void update_editable(){
if(menu==MENU_MAIN){
move(0,67);
attron(A_BOLD);
if(!editable)
addstr(" EDIT LOCKED ");
else
addstr(" ");
attroff(A_BOLD);
}
}
void move_next_cue(){
if(cue_list_number<cue_count){
while(++cue_list_number<cue_count)
if(cue_list[cue_list_number].tag==-1)break;
cue_list_position++;
}
main_update_cues(10+MAX_CHANNELS);
}
void move_prev_cue(){
if(cue_list_number>0){
while(--cue_list_number>0)
if(cue_list[cue_list_number].tag==-1)break;
cue_list_position--;
}
main_update_cues(10+MAX_CHANNELS);
}
int save_top_level(char *fn){
FILE *f;
if(!firstsave){
char *buf=alloca(strlen(fn)*2+20);
sprintf(buf,"cp %s %s~",fn,fn);
/* create backup file */
system(buf);
firstsave=1;
}
move(0,0);
attron(A_BOLD);
f=fopen(fn,"w");
if(f==NULL){
char buf[80];
sprintf(buf,"SAVE FAILED: %s",strerror(ferror(f)));
addnlstr(buf,80,' ');
attroff(A_BOLD);
return(1);
}
if(save_program(f)){
char buf[80];
sprintf(buf,"SAVE FAILED: %s",strerror(ferror(f)));
addnlstr(buf,80,' ');
attroff(A_BOLD);
fclose(f);
return(1);
}
fclose(f);
addnlstr("PROGRAM SAVED (any key to continue)",80,' ');
attroff(A_BOLD);
unsaved=0;
return(0);
}
int main_menu(){
clear();
move(0,0);
addstr("MTG Beaverphonic build "VERSION": ");
attron(A_BOLD);
addstr(program);
attroff(A_BOLD);
update_editable();
mvvline(3,2,0,MAX_CHANNELS+5);
mvvline(3,77,0,MAX_CHANNELS+5);
mvhline(2,2,0,76);
mvhline(7,2,0,76);
mvhline(8+MAX_CHANNELS,2,0,76);
mvaddch(2,2,ACS_ULCORNER);
mvaddch(2,77,ACS_URCORNER);
mvaddch(8+MAX_CHANNELS,2,ACS_LLCORNER);
mvaddch(8+MAX_CHANNELS,77,ACS_LRCORNER);
mvaddch(7,2,ACS_LTEE);
mvaddch(7,77,ACS_RTEE);
move(2,7);
addstr(" output ");
<p> mvhline(9+MAX_CHANNELS,0,0,80);
mvhline(16+MAX_CHANNELS,0,0,80);
main_update_master(main_master_volume,5);
main_update_playbuffer(4);
main_update_outchannel_labels(7);
main_update_cues(10+MAX_CHANNELS);
main_update_tags(17+MAX_CHANNELS);
curs_set(0);
refresh();
while(1){
int ch=getch();
switch(ch){
case '?':
return(MENU_KEYPRESS);
case 'q':
move(0,0);
attron(A_BOLD);
addnlstr("Really quit? [y/N] ",80,' ');
attroff(A_BOLD);
refresh();
ch=getch();
if(ch=='y'){
if(unsaved){
move(0,0);
attron(A_BOLD);
addnlstr("Save changes first? [Y/n] ",80,' ');
attroff(A_BOLD);
ch=getch();
if(ch!='n' && ch!='N')save_top_level(program);
}
return(MENU_QUIT);
}
move(0,0);
addstr("MTG Beaverphonic build "VERSION": ");
attron(A_BOLD);
addstr(program);
attroff(A_BOLD);
break;
case 'e':
if(editable && cue_list_number<cue_count)return(MENU_EDIT);
break;
case 'a':
if(editable)
return(MENU_ADD);
break;
case 'd':
if(editable){
//halt_playback();
move(0,0);
attron(A_BOLD);
addnlstr("Really delete cue? [y/N] ",80,' ');
attroff(A_BOLD);
refresh();
ch=getch();
if(ch=='y'){
unsaved=1;
delete_cue_bank(cue_list_number);
main_update_cues(10+MAX_CHANNELS);
}
move(0,0);
addstr("MTG Beaverphonic build "VERSION": ");
attron(A_BOLD);
addstr(program);
attroff(A_BOLD);
}
break;
case 'o':
if(editable)
return(MENU_OUTPUT);
break;
case 's':
save_top_level(program);
getch();
return(MENU_MAIN);
case '-':case '_':
unsaved=1;
main_update_master(main_master_volume-1,5);
break;
case '+':case '=':
unsaved=1;
main_update_master(main_master_volume+1,5);
break;
case ' ':
//run_next_cue();
move_next_cue();
break;
case KEY_UP:case '\b':case KEY_BACKSPACE:
move_prev_cue();
break;
case KEY_DOWN:
move_next_cue();
break;
case 'H':
//halt_playback();
break;
case 'R':
//halt_playback();
cue_list_position=0;
cue_list_number=0;
return(MENU_MAIN);
case 'l':
if(editable)
editable=0;
else
editable=1;
update_editable();
}
}
}
int main_keypress_menu(){
clear();
box(stdscr,0,0);
mvaddstr(0,2," Keypresses for main menu ");
attron(A_BOLD);
mvaddstr(2,2, " ?");
mvaddstr(4,2, " space");
mvaddstr(5,2, " up/down");
mvaddstr(6,2, "backspace");
mvaddstr(8,2, " a");
mvaddstr(9,2, " e");
mvaddstr(10,2," d");
mvaddstr(11,2," o");
mvaddstr(12,2," l");
mvaddstr(14,2," s");
mvaddstr(15,2," +/-");
mvaddstr(16,2," H");
mvaddstr(17,2," R");
attroff(A_BOLD);
mvaddstr(2,12,"keypress menu (you're there now)");
mvaddstr(4,12,"play next cue");
mvaddstr(5,12,"move cursor to prev/next cue");
mvaddstr(6,12,"move cursor to previous cue");
mvaddstr(8,12,"add new cue at current cursor position");
mvaddstr(9,12,"edit cue at current cursor position");
mvaddstr(10,12,"delete cue at current cursor position");
mvaddstr(11,12,"output channel configuration");
mvaddstr(12,12,"lock non-modifiable mode (production)");
mvaddstr(14,12,"save program");
mvaddstr(15,12,"master volume up/down");
mvaddstr(16,12,"halt playback");
mvaddstr(17,12,"reset board to beginning");
mvaddstr(19,12,"any key to return to main menu");
refresh();
getch();
return(MENU_MAIN);
}
int add_cue_menu(){
int tnum=new_tag_number();
form f;
char label[12]="";
char text[61]="";
char desc[61]="";
form_init(&f,4);
clear();
box(stdscr,0,0);
mvaddstr(0,2," Add new cue ");
mvaddstr(2,2," cue label");
mvaddstr(3,2," cue text");
mvaddstr(4,2,"cue description");
field_add(&f,FORM_GENERIC,18,2,12,label);
field_add(&f,FORM_GENERIC,18,3,59,text);
field_add(&f,FORM_GENERIC,18,4,59,desc);
field_add(&f,FORM_BUTTON,68,6,9,"ADD CUE");
refresh();
while(1){
int ch=form_handle_char(&f,getch());
switch(ch){
case KEY_ENTER:case 'x':case 'X':
goto enter;
case 27: /* esc */
goto out;
}
}
enter:
{
/* determine where in list cue is being added */
cue c;
unsaved=1;
memset(&c,0,sizeof(c));
c.tag=-1; /* placeholder cue for this cue bank */
/* aquire label locks, populate */
c.label=new_label_number();
edit_label(c.label,label);
aquire_label(c.label);
c.cue_text=new_label_number();
edit_label(c.cue_text,text);
aquire_label(c.cue_text);
c.cue_desc=new_label_number();
edit_label(c.cue_desc,desc);
aquire_label(c.cue_desc);
add_cue(cue_list_number,c);
move_next_cue();
}
out:
form_clear(&f);
return(MENU_MAIN);
}
int add_sample_cue_menu(){
int tnum=new_tag_number();
int i;
char buf[50];
form f;
form_init(&f,9+MAX_CHANNELS*3);
clear();
box(stdscr,0,0);
mvaddstr(0,2," Add new sample cue ");
mvaddstr(2,2,"cue number");
mvaddstr(3,2,"cue decimal");
mvaddstr(4,2,"sample tag");
sprintf(buf,"%4d",tnum);
attron(A_BOLD);
mvaddstr(4,18,buf);
attroff(A_BOLD);
mvaddstr(5,2,"cue text");
mvaddstr(7,2,"sample file");
mvaddstr(9,2,"volume master % fade / ms");
mvaddstr(10,2,"loop crosslap ms");
mvaddstr(12,2,"channels out");
for(i=0;i<MAX_CHANNELS;i++){
sprintf(buf,"L %% R -> %d ",i);
mvaddstr(12+i,17,buf);
addnlstr(channel_list[i].label,40,' ');
}
{
int number=0;
int decimal=0;
char text[60]="\0";
char path[60]="\0";
int master=100;
int masterin=15;
int masterout=15;
int outvol[2][MAX_CHANNELS]={{100,0},{0,100}};
int loop=0;
int cross=0;
int loopfield,crossfield;
field_add(&f,FORM_NUMBER,17,2,6,&number);
field_add(&f,FORM_NUMBER,17,3,6,&decimal);
field_add(&f,FORM_GENERIC,17,5,60,text);
field_add(&f,FORM_GENERIC,17,7,60,path);
field_add(&f,FORM_PERCENTAGE,17,9,5,&master);
field_add(&f,FORM_NUMBER,29,9,6,&masterin);
field_add(&f,FORM_NUMBER,36,9,6,&masterout);
loopfield=field_add(&f,FORM_YESNO,17,10,5,&loop);
crossfield=field_add(&f,FORM_NUMBER,36,10,6,&cross);
field_state(&f,crossfield,0);
for(i=0;i<MAX_CHANNELS;i++){
field_add(&f,FORM_PERCENTAGE,18,12+i,5,&(outvol[0][i]));
field_add(&f,FORM_PERCENTAGE,24,12+i,5,&(outvol[1][i]));
}
refresh();
while(1){
int ch=form_handle_char(&f,getch());
switch(ch){
case 27: /* esc */
goto out;
case KEY_ENTER: case '\n':case '\r':
{
}
return(MENU_MAIN);
}
if(f.cursor==loopfield){
if(loop)
field_state(&f,crossfield,1);
else
field_state(&f,crossfield,0);
}
}
}
out:
form_clear(&f);
return(MENU_MAIN);
}
void edit_keypress_menu(){
clear();
box(stdscr,0,0);
mvaddstr(0,2," Keypresses for cue edit menu ");
attron(A_BOLD);
mvaddstr(2,2, " ?");
mvaddstr(3,2, " up/down");
mvaddstr(4,2, " tab");
mvaddstr(5,2, " x");
mvaddstr(7,2, " a");
mvaddstr(8,2, " m");
mvaddstr(9,2, " d");
mvaddstr(10,2," enter");
mvaddstr(11,2," l");
attroff(A_BOLD);
mvaddstr(2,12,"keypress menu (you're there now)");
mvaddstr(3,12,"move cursor to prev/next field");
mvaddstr(4,12,"move cursor to next field");
mvaddstr(5,12,"return to main menu");
mvaddstr(7,12,"add new sample to cue");
mvaddstr(8,12,"add new mixer change to cue");
mvaddstr(9,12,"delete highlighted action");
mvaddstr(10,12,"edit highlighted action");
mvaddstr(11,12,"list all sample and sample tags");
mvaddstr(13,12,"any key to return to cue edit menu");
refresh();
getch();
}
int edit_cue_menu(){
form f;
char label[12]="";
char text[61]="";
char desc[61]="";
/* determine first and last cue in bank */
int base=cue_list_number;
int first=base+1;
int last=first;
int actions,i;
while(cue_list[last].tag!=-1)last++;
actions=last-first;
form_init(&f,4+actions);
strcpy(label,label_list[cue_list[base].label].text);
strcpy(text,label_list[cue_list[base].cue_text].text);
strcpy(desc,label_list[cue_list[base].cue_desc].text);
field_add(&f,FORM_GENERIC,18,2,12,label);
field_add(&f,FORM_GENERIC,18,3,59,text);
field_add(&f,FORM_GENERIC,18,4,59,desc);
for(i=0;i<actions;i++){
char *buf=alloca(81);
int tag=cue_list[first+i].tag;
snprintf(buf,80,"%s sample %d (%s)",
cue_list[first+i].tag_create_p?"ADD":"MIX",tag,
label_list[tag_list[tag].sample_path].text);
field_add(&f,FORM_BUTTON,11,6+i,64,buf);
}
if(actions==0){
mvaddstr(6,11,"--None--");
i++;
}
field_add(&f,FORM_BUTTON,66,7+i,11,"MAIN MENU");
refresh();
while(1){
int loop=1;
clear();
box(stdscr,0,0);
mvaddstr(0,2," Cue edit ");
mvaddstr(2,2," cue label");
mvaddstr(3,2," cue text");
mvaddstr(4,2,"cue description");
mvaddstr(6,2,"actions:");
form_redraw(&f);
while(loop){
int ch=form_handle_char(&f,getch());
switch(ch){
case '?':case '/':
edit_keypress_menu();
loop=0;
break;
case 27:
goto out;
case 'x':case 'X':
unsaved=1;
edit_label(cue_list[base].label,label);
edit_label(cue_list[base].cue_text,text);
edit_label(cue_list[base].cue_desc,desc);
goto out;
case KEY_ENTER:
if(f.cursor==3+actions){
unsaved=1;
edit_label(cue_list[base].label,label);
edit_label(cue_list[base].cue_text,text);
edit_label(cue_list[base].cue_desc,desc);
goto out;
}
/* ... else we're an action edit */
{
}
break;
}
}
}
out:
form_clear(&f);
return(MENU_MAIN);
}
int menu_output(){
return(MENU_MAIN);
}
int main_loop(){
while(running){
switch(menu){
case MENU_MAIN:
menu=main_menu();
break;
case MENU_KEYPRESS:
menu=main_keypress_menu();
break;
case MENU_QUIT:
running=0;
break;
case MENU_ADD:
menu=add_cue_menu();
break;
case MENU_OUTPUT:
menu=menu_output();
break;
case MENU_EDIT:
//halt_playback();
menu=edit_cue_menu();
break;
}
}
}
int main(int gratuitously,char *different[]){
if(gratuitously<2){
fprintf(stderr,"Usage: beaverphonic <settingfile>\n");
exit(1);
}
initscr(); cbreak(); noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
use_default_colors();
signal(SIGINT,SIG_IGN);
/* load the sound config if the file exists, else create it */
program=strdup(different[1]);
{
FILE *f=fopen(program,"rb");
if(f){
load_program(f);
fclose(f);
}
}
main_loop();
endwin(); /* restore original tty modes */
}
<p>#if 0
Add new cue -----------------------------------------------------------------
cue label [ ]
cue text [ ]
cue description [ ]
Edit cue --------------------------------------------------------------------
cue label [ ]
cue text [ ]
cue description [ ]
actions: [MIX sample 1 ( ) ]
[ADD sample 3 ( ) ]
?
a add sample
m mix sample
l list samples
e/enter edit action
Esc/Enter off main menu
Add new sample to cue -------------------------------------------------------
cue label [ ]
cue text [ ]
cue description [ ]
sample path [ ]
sample tag 0
loop [No ] crosslap [----]ms
volume master [100]% fade [ 15]/[ 15]ms
--- L -- R -----------------------------------------------------------------
| [100][ 0]% -> 0 offstage left |
| [ 0][100]% -> 1 offstage right |
| [ 0][ 0]% -> 2 |
| [ 0][ 0]% -> 3 |
| [ 0][ 0]% -> 4 |
| [ 0][ 0]% -> 5 |
| [ 0][ 0]% -> 6 |
| [ 0][ 0]% -> 7 |
----------------------------------------------------------------------------
Add mix change to cue -------------------------------------------------------
cue label [ ]
cue text [ ]
cue description [ ]
modify tag [ 0]
volume master [100]% fade [ 15]
--- L -- R -----------------------------------------------------------------
| [100][ 0]% -> 0 offstage left |
| [ 0][100]% -> 1 offstage right |
| [ 0][ 0]% -> 2 |
| [ 0][ 0]% -> 3 |
| [ 0][ 0]% -> 4 |
| [ 0][ 0]% -> 5 |
| [ 0][ 0]% -> 6 |
| [ 0][ 0]% -> 7 |
----------------------------------------------------------------------------
(l: list tags)
OUTPUT CHANNELS
0: [ ] built in OSS left
1: [ ] built in OSS right
2: [ ] Quattro 0
<p>#endif
<p><p><p>--- >8 ----
List archives: http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body. No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.
More information about the commits
mailing list