Mercurial > audlegacy-plugins
diff src/metronom/metronom.c @ 315:2294f3a6f136 trunk
[svn] Metronom (a.k.a. tact generator) input plugin, ported from XMMS.
author | chainsaw |
---|---|
date | Wed, 29 Nov 2006 14:42:11 -0800 |
parents | |
children | 7385182ae4b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/metronom/metronom.c Wed Nov 29 14:42:11 2006 -0800 @@ -0,0 +1,296 @@ +/* + * Copyright 2000 Martin Strauß <mys@faveve.uni-stuttgart.de> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "audacious/plugin.h" +#include "audacious/util.h" +#include <glib.h> +#include <string.h> + +#define MIN_BPM 1 +#define MAX_BPM 512 + +static InputPlugin metronom_ip; + +static gboolean going; +static gboolean audio_error; +static GThread *play_thread; + +struct metronom_struct { + gint bpm; + gint num; + gint den; + gint id; +}; +typedef struct metronom_struct metronom_t; + +#define tact_id_max 12 +gint tact_id[tact_id_max][2]= + { + {1,1}, + {2,2}, + {3,2}, + {4,2}, + {2,4}, + {3,4}, + {4,4}, + {6,4}, + {2,8}, + {3,8}, + {4,8}, + {6,8} + }; +#define tact_form_max 8 +gdouble tact_form[tact_id_max][tact_form_max]= + { + {1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.0,0.0,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.5,0.0,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.6,0.5,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.0,0.0,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.5,0.0,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.6,0.5,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.5,0.6,0.5,0.5,0.0,0.0}, + {1.0,0.5,0.0,0.0,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.5,0.0,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.6,0.5,0.0,0.0,0.0,0.0}, + {1.0,0.5,0.5,0.6,0.5,0.5,0.0,0.0} + }; + +static void metronom_about(void) +{ + static GtkWidget *box; + box = xmms_show_message( + "About Metronom", + "A Tact Generator by Martin Strauß <mys@faveve.uni-stuttgart.de>\n\nTo use it, add a URL: tact://beats*num/den\ne.g. tact://77 to play 77 beats per minute\nor tact://60*3/4 to play 60 bpm in 3/4 tacts", "Ok", + FALSE, NULL, NULL); + gtk_signal_connect(GTK_OBJECT(box), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), &box); +} + +static int metronom_is_our_file(char *filename) +{ + if (!strncmp(filename, "tact://", 7)) + return TRUE; + return FALSE; +} + +#define BUF_SAMPLES 512 +#define BUF_BYTES BUF_SAMPLES * 2 +#define MAX_AMPL (GINT16_TO_LE((1<<15) - 1)) + +static void* play_loop(void *arg) +{ + gint16 data[BUF_SAMPLES]; + metronom_t *pmetronom=(metronom_t *)arg; + gint i; + + gint16 t = 0,tact; + gint16 datagoal = 0; + gint16 datamiddle = 0; + gint16 datacurrent = datamiddle; + gint16 datalast = datamiddle; + gint16 data_form[tact_form_max]; + gint num; + + tact = 60*44100/pmetronom->bpm; + /* prepare weighted amplitudes */ + for(num=0;num<pmetronom->num;num++){ + data_form[num]=MAX_AMPL*tact_form[pmetronom->id][num]; + } + + num=0; + while (going) + { + for (i = 0; i < BUF_SAMPLES; i++){ + if(t==tact){ + t=0; + datagoal = data_form[num]; + } + else if(t==10) { + datagoal = -data_form[num]; + } + else if(t==25) { + datagoal = data_form[num]; + /* circle through weighted amplitudes */ + num++; + if(num==pmetronom->num)num=0; + } + /* makes curve a little bit smoother */ + data[i]=(datalast+datacurrent+datagoal)/3; + datalast=datacurrent; + datacurrent=data[i]; + if(t > 35) + datagoal=(datamiddle+7*datagoal)/8; + t++; + } + while(metronom_ip.output->buffer_free() < BUF_BYTES && going) + xmms_usleep(30000); + if (going) + produce_audio(metronom_ip.output->written_time(), FMT_S16_LE, 1, BUF_BYTES, data, &going); + } + /* Make sure the output plugin stops prebuffering */ + free(arg); + metronom_ip.output->buffer_free(); + metronom_ip.output->buffer_free(); + g_thread_exit(NULL); +} + +static void metronom_play(char *filename) +{ + gchar *name; + size_t count; + metronom_t *pmetronom; + gint flag,id; + + pmetronom=(metronom_t *)malloc(sizeof(metronom_t)); + if(!pmetronom)return; + + count=sscanf(filename, "tact://%d*%d/%d", &pmetronom->bpm,&pmetronom->num,&pmetronom->den); + if (count != 1 && count !=3)return; + if(!(pmetronom->bpm >= MIN_BPM && pmetronom->bpm <= MAX_BPM))return; + pmetronom->id=0; + if(count==1){ + pmetronom->num=1; + pmetronom->den=1; + } else { + if(pmetronom->num==0 || pmetronom->den==0)return; + flag=FALSE; + for(id=0;(id<tact_id_max && (!flag));id++){ + if(pmetronom->num==tact_id[id][0] && pmetronom->den==tact_id[id][1]){ + flag=TRUE; + pmetronom->id=id; + } + } + if(!flag)return; + } + + going = TRUE; + audio_error = FALSE; + if (metronom_ip.output->open_audio(FMT_S16_LE, 44100, 1) == 0) + { + audio_error = TRUE; + going = FALSE; + return; + } + if(pmetronom->num==1 && pmetronom->den==1){ + name = g_strdup_printf("Tact generator: %d bpm", pmetronom->bpm); + } else { + name = g_strdup_printf("Tact generator: %d bpm %d/%d", pmetronom->bpm,pmetronom->num,pmetronom->den); + } + metronom_ip.set_info(name, -1, 16 * 44100, 44100, 1); + g_free(name); + play_thread = g_thread_create((GThreadFunc)play_loop, pmetronom, TRUE, NULL); +} + +static void metronom_stop(void) +{ + if (going) + { + going = FALSE; + g_thread_join(play_thread); + metronom_ip.output->close_audio(); + } +} + +static void metronom_pause(short paused) +{ + metronom_ip.output->pause(paused); +} + +static int metronom_get_time(void) +{ + if (audio_error) + return -2; + if (!going && !metronom_ip.output->buffer_playing()) + return -1; + return metronom_ip.output->output_time(); +} + +static void metronom_song_info(char *filename, char **title, int *length) +{ + metronom_t metronom; + metronom_t *pmetronom=&metronom; + size_t count; + gint flag,id; + *length = -1; + *title = NULL; + + count=sscanf(filename, "tact://%d*%d/%d", &pmetronom->bpm,&pmetronom->num,&pmetronom->den); + if (count != 1 && count !=3)return; + if(!(pmetronom->bpm >= MIN_BPM && pmetronom->bpm <= MAX_BPM))return; + + if (count == 1) { + pmetronom->num=1; + pmetronom->den=1; + pmetronom->id=0; + } else { + if(pmetronom->num==0 || pmetronom->den==0)return; + flag=FALSE; + for(id=0;(id<tact_id_max && (!flag));id++){ + if(pmetronom->num==tact_id[id][0] && pmetronom->den==tact_id[id][1])flag=TRUE; + } + if(!flag)return; + else pmetronom->id=id; + } + + if(pmetronom->num==1 && pmetronom->den==1){ + *title = g_strdup_printf("Tact generator: %d bpm", pmetronom->bpm); + } else { + *title = g_strdup_printf("Tact generator: %d bpm %d/%d", pmetronom->bpm,pmetronom->num,pmetronom->den); + } +} + + + +static InputPlugin metronom_ip = +{ + NULL, + NULL, + "Tact Generator " VERSION, + NULL, + metronom_about, + NULL, + metronom_is_our_file, + NULL, + metronom_play, + metronom_stop, + metronom_pause, + NULL, + NULL, + metronom_get_time, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + metronom_song_info, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +InputPlugin *get_iplugin_info(void) +{ + return &metronom_ip; +}