comparison 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
comparison
equal deleted inserted replaced
314:29e5e3b6f1b6 315:2294f3a6f136
1 /*
2 * Copyright 2000 Martin Strauß <mys@faveve.uni-stuttgart.de>
3 *
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #include "audacious/plugin.h"
21 #include "audacious/util.h"
22 #include <glib.h>
23 #include <string.h>
24
25 #define MIN_BPM 1
26 #define MAX_BPM 512
27
28 static InputPlugin metronom_ip;
29
30 static gboolean going;
31 static gboolean audio_error;
32 static GThread *play_thread;
33
34 struct metronom_struct {
35 gint bpm;
36 gint num;
37 gint den;
38 gint id;
39 };
40 typedef struct metronom_struct metronom_t;
41
42 #define tact_id_max 12
43 gint tact_id[tact_id_max][2]=
44 {
45 {1,1},
46 {2,2},
47 {3,2},
48 {4,2},
49 {2,4},
50 {3,4},
51 {4,4},
52 {6,4},
53 {2,8},
54 {3,8},
55 {4,8},
56 {6,8}
57 };
58 #define tact_form_max 8
59 gdouble tact_form[tact_id_max][tact_form_max]=
60 {
61 {1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
62 {1.0,0.5,0.0,0.0,0.0,0.0,0.0,0.0},
63 {1.0,0.5,0.5,0.0,0.0,0.0,0.0,0.0},
64 {1.0,0.5,0.6,0.5,0.0,0.0,0.0,0.0},
65 {1.0,0.5,0.0,0.0,0.0,0.0,0.0,0.0},
66 {1.0,0.5,0.5,0.0,0.0,0.0,0.0,0.0},
67 {1.0,0.5,0.6,0.5,0.0,0.0,0.0,0.0},
68 {1.0,0.5,0.5,0.6,0.5,0.5,0.0,0.0},
69 {1.0,0.5,0.0,0.0,0.0,0.0,0.0,0.0},
70 {1.0,0.5,0.5,0.0,0.0,0.0,0.0,0.0},
71 {1.0,0.5,0.6,0.5,0.0,0.0,0.0,0.0},
72 {1.0,0.5,0.5,0.6,0.5,0.5,0.0,0.0}
73 };
74
75 static void metronom_about(void)
76 {
77 static GtkWidget *box;
78 box = xmms_show_message(
79 "About Metronom",
80 "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",
81 FALSE, NULL, NULL);
82 gtk_signal_connect(GTK_OBJECT(box), "destroy",
83 GTK_SIGNAL_FUNC(gtk_widget_destroyed), &box);
84 }
85
86 static int metronom_is_our_file(char *filename)
87 {
88 if (!strncmp(filename, "tact://", 7))
89 return TRUE;
90 return FALSE;
91 }
92
93 #define BUF_SAMPLES 512
94 #define BUF_BYTES BUF_SAMPLES * 2
95 #define MAX_AMPL (GINT16_TO_LE((1<<15) - 1))
96
97 static void* play_loop(void *arg)
98 {
99 gint16 data[BUF_SAMPLES];
100 metronom_t *pmetronom=(metronom_t *)arg;
101 gint i;
102
103 gint16 t = 0,tact;
104 gint16 datagoal = 0;
105 gint16 datamiddle = 0;
106 gint16 datacurrent = datamiddle;
107 gint16 datalast = datamiddle;
108 gint16 data_form[tact_form_max];
109 gint num;
110
111 tact = 60*44100/pmetronom->bpm;
112 /* prepare weighted amplitudes */
113 for(num=0;num<pmetronom->num;num++){
114 data_form[num]=MAX_AMPL*tact_form[pmetronom->id][num];
115 }
116
117 num=0;
118 while (going)
119 {
120 for (i = 0; i < BUF_SAMPLES; i++){
121 if(t==tact){
122 t=0;
123 datagoal = data_form[num];
124 }
125 else if(t==10) {
126 datagoal = -data_form[num];
127 }
128 else if(t==25) {
129 datagoal = data_form[num];
130 /* circle through weighted amplitudes */
131 num++;
132 if(num==pmetronom->num)num=0;
133 }
134 /* makes curve a little bit smoother */
135 data[i]=(datalast+datacurrent+datagoal)/3;
136 datalast=datacurrent;
137 datacurrent=data[i];
138 if(t > 35)
139 datagoal=(datamiddle+7*datagoal)/8;
140 t++;
141 }
142 while(metronom_ip.output->buffer_free() < BUF_BYTES && going)
143 xmms_usleep(30000);
144 if (going)
145 produce_audio(metronom_ip.output->written_time(), FMT_S16_LE, 1, BUF_BYTES, data, &going);
146 }
147 /* Make sure the output plugin stops prebuffering */
148 free(arg);
149 metronom_ip.output->buffer_free();
150 metronom_ip.output->buffer_free();
151 g_thread_exit(NULL);
152 }
153
154 static void metronom_play(char *filename)
155 {
156 gchar *name;
157 size_t count;
158 metronom_t *pmetronom;
159 gint flag,id;
160
161 pmetronom=(metronom_t *)malloc(sizeof(metronom_t));
162 if(!pmetronom)return;
163
164 count=sscanf(filename, "tact://%d*%d/%d", &pmetronom->bpm,&pmetronom->num,&pmetronom->den);
165 if (count != 1 && count !=3)return;
166 if(!(pmetronom->bpm >= MIN_BPM && pmetronom->bpm <= MAX_BPM))return;
167 pmetronom->id=0;
168 if(count==1){
169 pmetronom->num=1;
170 pmetronom->den=1;
171 } else {
172 if(pmetronom->num==0 || pmetronom->den==0)return;
173 flag=FALSE;
174 for(id=0;(id<tact_id_max && (!flag));id++){
175 if(pmetronom->num==tact_id[id][0] && pmetronom->den==tact_id[id][1]){
176 flag=TRUE;
177 pmetronom->id=id;
178 }
179 }
180 if(!flag)return;
181 }
182
183 going = TRUE;
184 audio_error = FALSE;
185 if (metronom_ip.output->open_audio(FMT_S16_LE, 44100, 1) == 0)
186 {
187 audio_error = TRUE;
188 going = FALSE;
189 return;
190 }
191 if(pmetronom->num==1 && pmetronom->den==1){
192 name = g_strdup_printf("Tact generator: %d bpm", pmetronom->bpm);
193 } else {
194 name = g_strdup_printf("Tact generator: %d bpm %d/%d", pmetronom->bpm,pmetronom->num,pmetronom->den);
195 }
196 metronom_ip.set_info(name, -1, 16 * 44100, 44100, 1);
197 g_free(name);
198 play_thread = g_thread_create((GThreadFunc)play_loop, pmetronom, TRUE, NULL);
199 }
200
201 static void metronom_stop(void)
202 {
203 if (going)
204 {
205 going = FALSE;
206 g_thread_join(play_thread);
207 metronom_ip.output->close_audio();
208 }
209 }
210
211 static void metronom_pause(short paused)
212 {
213 metronom_ip.output->pause(paused);
214 }
215
216 static int metronom_get_time(void)
217 {
218 if (audio_error)
219 return -2;
220 if (!going && !metronom_ip.output->buffer_playing())
221 return -1;
222 return metronom_ip.output->output_time();
223 }
224
225 static void metronom_song_info(char *filename, char **title, int *length)
226 {
227 metronom_t metronom;
228 metronom_t *pmetronom=&metronom;
229 size_t count;
230 gint flag,id;
231 *length = -1;
232 *title = NULL;
233
234 count=sscanf(filename, "tact://%d*%d/%d", &pmetronom->bpm,&pmetronom->num,&pmetronom->den);
235 if (count != 1 && count !=3)return;
236 if(!(pmetronom->bpm >= MIN_BPM && pmetronom->bpm <= MAX_BPM))return;
237
238 if (count == 1) {
239 pmetronom->num=1;
240 pmetronom->den=1;
241 pmetronom->id=0;
242 } else {
243 if(pmetronom->num==0 || pmetronom->den==0)return;
244 flag=FALSE;
245 for(id=0;(id<tact_id_max && (!flag));id++){
246 if(pmetronom->num==tact_id[id][0] && pmetronom->den==tact_id[id][1])flag=TRUE;
247 }
248 if(!flag)return;
249 else pmetronom->id=id;
250 }
251
252 if(pmetronom->num==1 && pmetronom->den==1){
253 *title = g_strdup_printf("Tact generator: %d bpm", pmetronom->bpm);
254 } else {
255 *title = g_strdup_printf("Tact generator: %d bpm %d/%d", pmetronom->bpm,pmetronom->num,pmetronom->den);
256 }
257 }
258
259
260
261 static InputPlugin metronom_ip =
262 {
263 NULL,
264 NULL,
265 "Tact Generator " VERSION,
266 NULL,
267 metronom_about,
268 NULL,
269 metronom_is_our_file,
270 NULL,
271 metronom_play,
272 metronom_stop,
273 metronom_pause,
274 NULL,
275 NULL,
276 metronom_get_time,
277 NULL,
278 NULL,
279 NULL,
280 NULL,
281 NULL,
282 NULL,
283 NULL,
284 metronom_song_info,
285 NULL,
286 NULL,
287 NULL,
288 NULL,
289 NULL,
290 NULL,
291 };
292
293 InputPlugin *get_iplugin_info(void)
294 {
295 return &metronom_ip;
296 }