summaryrefslogtreecommitdiffabout
path: root/gammu/emb/common/service/gsmring.c
Unidiff
Diffstat (limited to 'gammu/emb/common/service/gsmring.c') (more/less context) (ignore whitespace changes)
-rw-r--r--gammu/emb/common/service/gsmring.c1600
1 files changed, 1600 insertions, 0 deletions
diff --git a/gammu/emb/common/service/gsmring.c b/gammu/emb/common/service/gsmring.c
new file mode 100644
index 0000000..5a1ff87
--- a/dev/null
+++ b/gammu/emb/common/service/gsmring.c
@@ -0,0 +1,1600 @@
1/* (c) 2001-2004 by Marcin Wiacek */
2/* Based on some work from Ralf Thelen (7110 ringtones),
3 * Gnokii (RTTL and SM) and others
4 */
5
6#include <stdlib.h>
7#include <string.h>
8#include <ctype.h>
9#include <math.h>
10#ifdef WIN32
11# include <windows.h>
12#endif
13
14#include "../gsmcomon.h"
15#include "../misc/coding/coding.h"
16#include "../gsmstate.h"
17#include "gsmring.h"
18#include "sms/gsmsms.h"
19
20int GSM_RingNoteGetFrequency(GSM_RingNote Note)
21{
22 double freq=0;
23
24 /* Values according to the software from http://iki.fi/too/sw/xring/
25 * generated with:
26 * perl -e 'print int(4400 * (2 **($_/12)) + .5)/10, "\n" for(3..14)'
27 */
28 switch (Note.Note) {
29 case Note_C : freq = 523.3; break;
30 case Note_Cis: freq = 554.4; break;
31 case Note_D : freq = 587.3; break;
32 case Note_Dis: freq = 622.3; break;
33 case Note_E : freq = 659.3; break;
34 case Note_F : freq = 698.5; break;
35 case Note_Fis: freq = 740; break;
36 case Note_G : freq = 784; break;
37 case Note_Gis: freq = 830.6; break;
38 case Note_A : freq = 880; break;
39 case Note_Ais: freq = 932.3; break;
40 case Note_H : freq = 987.8; break;
41 case Note_Pause: break;
42 }
43 switch (Note.Scale) {
44 case Scale_440 : freq = freq / 2; break;
45 case Scale_880 : break;
46 case Scale_1760: freq = freq * 2; break;
47 case Scale_3520: freq = freq * 4; break;
48 default : break;
49 }
50 return (int)freq;
51}
52
53int GSM_RingNoteGetFullDuration(GSM_RingNote Note)
54{
55 int duration = 1;
56
57 switch (Note.Duration) {
58 case Duration_Full : duration = 128; break;
59 case Duration_1_2 : duration = 64; break;
60 case Duration_1_4 : duration = 32; break;
61 case Duration_1_8 : duration = 16; break;
62 case Duration_1_16 : duration = 8; break;
63 case Duration_1_32 : duration = 4; break;
64 }
65 switch (Note.DurationSpec) {
66 case NoSpecialDuration : break;
67 case DottedNote : duration = duration * 3/2;break;
68 case DoubleDottedNote : duration = duration * 9/4;break;
69 case Length_2_3 : duration = duration * 2/3;break;
70 }
71 return duration;
72}
73
74#ifndef PI
75# define PI 3.141592654
76#endif
77
78#define WAV_SAMPLE_RATE 44100
79
80GSM_Error savewav(FILE *file, GSM_Ringtone *ringtone)
81{
82 unsigned char WAV_Header[] = {
83 'R','I','F','F',
84 0x00,0x00,0x00,0x00,/* Length */
85 'W','A','V','E'};
86 unsigned char FMT_Header[] = {'f','m','t',' ',
87 0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x44,0xac,
88 0x00,0x00,0x88,0x58,0x01,0x00,0x02,0x00,0x10,0x00};
89 unsigned char DATA_Header[] = {
90 'd','a','t','a',
91 0x00,0x00,0x00,0x00};/* Length */
92 short DATA_Buffer[60000];
93 long wavfilesize;
94 GSM_RingNote *Note;
95 long i,j,length=0;
96 double phase=0,phase_step;
97
98 fwrite(&WAV_Header, 1, sizeof(WAV_Header),file);
99 fwrite(&FMT_Header, 1, sizeof(FMT_Header),file);
100 fwrite(&DATA_Header, 1, sizeof(DATA_Header),file);
101
102 for (i=0;i<ringtone->NoteTone.NrCommands;i++) {
103 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
104 Note = &ringtone->NoteTone.Commands[i].Note;
105 phase_step = GSM_RingNoteGetFrequency(*Note)*WAV_SAMPLE_RATE*1.5;
106 for (j=0;j<((long)(GSM_RingNoteGetFullDuration(*Note)*WAV_SAMPLE_RATE/70));j++) {
107 DATA_Buffer[j] = ((int)(sin(phase*PI)*50000));
108 phase = phase + phase_step;
109 length++;
110 }
111 fwrite(&DATA_Buffer,sizeof(short),j,file);
112 }
113 }
114
115 wavfilesize = sizeof(WAV_Header) + sizeof(FMT_Header) + sizeof(DATA_Header) + length*2;
116 WAV_Header[4] = ((unsigned char)wavfilesize % 256);
117 WAV_Header[5] = ((unsigned char)wavfilesize / 256);
118 WAV_Header[6] = ((unsigned char)wavfilesize / (256*256));
119 WAV_Header[7] = ((unsigned char)wavfilesize / (256*256*256));
120 wavfilesize = wavfilesize - 54;
121 DATA_Header[4] = ((unsigned char)wavfilesize % 256);
122 DATA_Header[5] = ((unsigned char)wavfilesize / 256);
123 DATA_Header[6] = ((unsigned char)wavfilesize / (256*256));
124 DATA_Header[7] = ((unsigned char)wavfilesize / (256*256*256));
125
126 fseek( file, 0, SEEK_SET);
127 fwrite(&WAV_Header, 1, sizeof(WAV_Header),file);
128 fwrite(&FMT_Header, 1, sizeof(FMT_Header),file);
129 fwrite(&DATA_Header, 1, sizeof(DATA_Header),file);
130
131 return ERR_NONE;
132}
133
134static GSM_Error savebin(FILE *file, GSM_Ringtone *ringtone)
135{
136 char nullchar=0x00;
137
138 fwrite(&nullchar,1,1,file);
139 fwrite(&nullchar,1,1,file);
140 fprintf(file,"\x0C\x01\x2C");
141 fprintf(file,"%s",DecodeUnicodeString(ringtone->Name));
142 fwrite(&nullchar,1,1,file);
143 fwrite(&nullchar,1,1,file);
144 fwrite(ringtone->NokiaBinary.Frame,1,ringtone->NokiaBinary.Length,file);
145 return ERR_NONE;
146}
147
148static GSM_Error savepuremidi(FILE *file, GSM_Ringtone *ringtone)
149{
150 fwrite(ringtone->NokiaBinary.Frame,1,ringtone->NokiaBinary.Length,file);
151 return ERR_NONE;
152}
153
154GSM_Error saverttl(FILE *file, GSM_Ringtone *ringtone)
155{
156 GSM_RingNoteScaleDefNoteScale;
157 GSM_RingNoteDurationDefNoteDuration;
158
159 GSM_RingNoteStyleDefNoteStyle=0;
160 int DefNoteTempo=0;
161
162 bool started = false, firstcomma = true;
163 GSM_RingNote *Note;
164
165 unsigned char buffer[15];
166 int i,j,k=0;
167
168 /* Saves ringtone name */
169 fprintf(file,"%s:",DecodeUnicodeString(ringtone->Name));
170
171 /* Find the most frequently used duration */
172 for (i=0;i<6;i++) buffer[i]=0;
173 for (i=0;i<ringtone->NoteTone.NrCommands;i++) {
174 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
175 Note = &ringtone->NoteTone.Commands[i].Note;
176 /* some durations need 2 bytes in file, some 1 */
177 if (Note->Duration >= Duration_Full && Note->Duration <= Duration_1_8) {
178 buffer[Note->Duration/32]++;
179 }
180 if (Note->Duration >= Duration_1_16 && Note->Duration <= Duration_1_32) {
181 buffer[Note->Duration/32]+=2;
182 }
183 }
184 }
185 /* Now find the most frequently used */
186 j=0;
187 for (i=0;i<6;i++) {
188 if (buffer[i]>j) {
189 k=i;
190 j=buffer[i];
191 }
192 }
193 /* Finally convert the default duration */
194 DefNoteDuration = k * 32;
195 dbgprintf("DefNoteDuration=%d\n", DefNoteDuration);
196 switch (DefNoteDuration) {
197 case Duration_Full:fprintf(file,"d=1"); break;
198 case Duration_1_2 :fprintf(file,"d=2"); break;
199 case Duration_1_4 :fprintf(file,"d=4"); break;
200 case Duration_1_8 :fprintf(file,"d=8"); break;
201 case Duration_1_16:fprintf(file,"d=16");break;
202 case Duration_1_32:fprintf(file,"d=32");break;
203 }
204
205 /* Find the most frequently used scale */
206 for (i=0;i<9;i++) buffer[i]=0;
207 for (i=0;i<ringtone->NoteTone.NrCommands;i++) {
208 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
209 Note = &ringtone->NoteTone.Commands[i].Note;
210 if (Note->Note!=Note_Pause &&
211 Note->Scale >= Scale_55 && Note->Scale <= Scale_14080) {
212 buffer[Note->Scale - 1]++;
213 }
214 }
215 }
216 j=0;
217 for (i=0;i<9;i++) {
218 if (buffer[i]>j) {
219 k = i;
220 j=buffer[i];
221 }
222 }
223 DefNoteScale = k + 1;
224 /* Save the default scale */
225 fprintf(file,",o=%i,",DefNoteScale);
226 dbgprintf("DefNoteScale=%d\n", DefNoteScale);
227
228 for (i=0;i<ringtone->NoteTone.NrCommands;i++) {
229 if (ringtone->NoteTone.Commands[i].Type != RING_Note) continue;
230
231 Note = &ringtone->NoteTone.Commands[i].Note;
232
233 /* Trick from PPM Edit */
234 if (Note->DurationSpec == DoubleDottedNote) {
235 switch (Note->Duration) {
236 case Duration_Full:Note->Duration = Duration_Full;break;
237 case Duration_1_2 :Note->Duration = Duration_Full;break;
238 case Duration_1_4 :Note->Duration = Duration_1_2; break;
239 case Duration_1_8 :Note->Duration = Duration_1_4; break;
240 case Duration_1_16:Note->Duration = Duration_1_8; break;
241 case Duration_1_32:Note->Duration = Duration_1_16;break;
242 }
243 Note->DurationSpec = NoSpecialDuration;
244 }
245
246 if (!started) {
247 DefNoteTempo=Note->Tempo;
248 DefNoteStyle=Note->Style;
249 switch (Note->Style) {
250 case StaccatoStyle: fprintf(file,"s=S,"); break;
251 case NaturalStyle : fprintf(file,"s=N,"); break;
252 case ContinuousStyle : break;
253 }
254 /* Save the default tempo */
255 fprintf(file,"b=%i:",DefNoteTempo);
256 dbgprintf("DefNoteTempo=%d\n", DefNoteTempo);
257 started = true;
258 firstcomma = true;
259 }
260
261 if (!started) continue;
262
263 if (Note->Style!=DefNoteStyle) {
264 /* And a separator */
265 if (!firstcomma) fprintf(file,",");
266 firstcomma = false;
267 DefNoteStyle=Note->Style;
268 switch (Note->Style) {
269 case StaccatoStyle : fprintf(file,"s=S"); break;
270 case NaturalStyle : fprintf(file,"s=N"); break;
271 case ContinuousStyle: fprintf(file,"s=C"); break;
272 }
273 }
274 if (Note->Tempo!=DefNoteTempo) {
275 /* And a separator */
276 if (!firstcomma) fprintf(file,",");
277 firstcomma = false;
278 DefNoteTempo=Note->Tempo;
279 fprintf(file,"b=%i",DefNoteTempo);
280 }
281 /* This note has a duration different than the default. We must save it */
282 if (Note->Duration!=DefNoteDuration) {
283 /* And a separator */
284 if (!firstcomma) fprintf(file,",");
285 firstcomma = false;
286 switch (Note->Duration) {
287 case Duration_Full:fprintf(file,"1"); break;
288 case Duration_1_2 :fprintf(file,"2"); break;
289 case Duration_1_4 :fprintf(file,"4"); break;
290 case Duration_1_8 :fprintf(file,"8"); break;
291 case Duration_1_16:fprintf(file,"16");break;
292 case Duration_1_32:fprintf(file,"32");break;
293 }
294 } else {
295 /* And a separator */
296 if (!firstcomma) fprintf(file,",");
297 firstcomma = false;
298 }
299 /* Now save the actual note */
300 switch (Note->Note) {
301 case Note_C :fprintf(file,"c");break;
302 case Note_Cis:fprintf(file,"c#");break;
303 case Note_D :fprintf(file,"d");break;
304 case Note_Dis:fprintf(file,"d#");break;
305 case Note_E :fprintf(file,"e");break;
306 case Note_F :fprintf(file,"f");break;
307 case Note_Fis:fprintf(file,"f#");break;
308 case Note_G :fprintf(file,"g");break;
309 case Note_Gis:fprintf(file,"g#");break;
310 case Note_A :fprintf(file,"a");break;
311 case Note_Ais:fprintf(file,"a#");break;
312 case Note_H :fprintf(file,"h");break;
313 default :fprintf(file,"p");break; /*Pause ?*/
314 }
315 switch (Note->DurationSpec) {
316 case DottedNote : fprintf(file,".");break;
317 default : break;
318 }
319 if (Note->Note!=Note_Pause && Note->Scale != DefNoteScale) {
320 fprintf(file,"%i",Note->Scale);
321 }
322 }
323 return ERR_NONE;
324}
325
326void saveimelody(FILE *file, GSM_Ringtone *ringtone)
327{
328 char Buffer[2000];
329 int i=2000;
330
331 GSM_EncodeEMSSound(*ringtone, Buffer, &i, (float)1.2, true);
332
333 fwrite(Buffer, 1, i, file);
334}
335
336#ifndef ENABLE_LGPL
337
338static void WriteVarLen(unsigned char* midifile, int* current, long value)
339{
340 long buffer;
341
342 buffer = value & 0x7f;
343
344 while (value >>= 7) {
345 buffer <<= 8;
346 buffer |= 0x80;
347 buffer += (value & 0x7f);
348 }
349
350 while (1) {
351 midifile[(*current)++] = (unsigned char)buffer;
352 if (buffer & 0x80) {
353 buffer >>= 8;
354 } else {
355 break;
356 }
357 }
358}
359
360#define singlepauses
361
362/* FIXME: need adding tempo before each note and scale too ? */
363void savemid(FILE* file, GSM_Ringtone *ringtone)
364{
365 int pause = 0, current = 26, duration, i, note=0, length = 20;
366 bool started = false;
367 GSM_RingNote *Note;
368 unsigned char midifile[3000] = {
369 0x4D, 0x54, 0x68, 0x64, // MThd
370 0x00, 0x00, 0x00, 0x06, // chunk length
371 0x00, 0x00, // format 0
372 0x00, 0x01, // one track
373 0x00, 0x20, // 32 per quarter note
374 0x4D, 0x54, 0x72, 0x6B, // MTrk
375 0x00, 0x00, 0x00, 0x00, // chunk length
376 0x00, 0xFF, 0x51, 0x03, // tempo meta event
377 0x00, 0x00, 0x00}; // 3 bytes for us for a quarter note
378
379 for (i = 0; i < ringtone->NoteTone.NrCommands; i++) {
380 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
381 Note = &ringtone->NoteTone.Commands[i].Note;
382 if (!started) {
383 /* readmid does not read pauses at the beginning */
384 if (Note->Note != Note_Pause) {
385 /* FIXME: we need add tempo before each note or so... */
386 long duration=60000000/Note->Tempo;
387
388 midifile[current++] = (unsigned char)(duration >> 16);
389 midifile[current++] = (unsigned char)(duration >> 8);
390 midifile[current++] = (unsigned char)duration;
391
392 started = true;
393 }
394 }
395 if (!started) continue;
396 duration = GSM_RingNoteGetFullDuration(*Note);
397 if (Note->Note == Note_Pause) {
398 pause += duration;
399#ifdef singlepauses
400 WriteVarLen(midifile,&current,pause);
401 pause=0;
402 midifile[current++]=0x00; // pause
403 midifile[current++]=0x00;
404#endif
405 } else {
406 if (Note->Note >= Note_C && Note->Note <= Note_H) {
407 note = Note->Note/16 + 12 * Note->Scale - 1;
408 }
409
410 WriteVarLen(midifile,&current,pause);
411 pause=0;
412 midifile[current++]=0x90; // note on
413 midifile[current++]=note;
414 midifile[current++]=0x64; // forte
415
416 WriteVarLen(midifile,&current,duration);
417 midifile[current++]=0x80; // note off
418 midifile[current++]=note;
419 midifile[current++]=0x64;
420 }
421 }
422 }
423 if (pause) {
424 WriteVarLen(midifile,&current,pause);
425 midifile[current++]=0x00; // pause
426 midifile[current++]=0x00; //
427 }
428 midifile[current++] = 0x00;
429 midifile[current++] = 0xFF; // track end
430 midifile[current++] = 0x2F;
431 midifile[current++] = 0x00;
432 midifile[length++] = (current-22) >> 8;
433 midifile[length++] = current-22;
434
435 fwrite(midifile,1,current,file);
436}
437
438#endif
439
440void saveott(FILE *file, GSM_Ringtone *ringtone)
441{
442 char Buffer[2000];
443 int i=2000;
444
445 GSM_EncodeNokiaRTTLRingtone(*ringtone, Buffer, &i);
446
447 fwrite(Buffer, 1, i, file);
448}
449
450GSM_Error GSM_SaveRingtoneFile(char *FileName, GSM_Ringtone *ringtone)
451{
452 FILE *file;
453
454 file = fopen(FileName, "wb");
455 if (file == NULL) return ERR_CANTOPENFILE;
456
457 switch (ringtone->Format) {
458 case RING_NOTETONE:
459 if (strstr(FileName,".ott")) {
460 saveott(file,ringtone);
461#ifndef ENABLE_LGPL
462 } else if (strstr(FileName,".mid")) {
463 savemid(file,ringtone);
464#endif
465 } else if (strstr(FileName,".rng")) {
466 saveott(file,ringtone);
467 } else if (strstr(FileName,".imy")) {
468 saveimelody(file,ringtone);
469 } else if (strstr(FileName,".ime")) {
470 saveimelody(file,ringtone);
471 } else if (strstr(FileName,".wav")) {
472 savewav(file,ringtone);
473 } else {
474 saverttl(file, ringtone);
475 }
476 break;
477 case RING_NOKIABINARY:
478 savebin(file, ringtone);
479 break;
480 case RING_MIDI:
481 savepuremidi(file, ringtone);
482 break;
483 }
484
485 fclose(file);
486
487 return ERR_NONE;
488}
489
490static GSM_Error loadrttl(FILE *file, GSM_Ringtone *ringtone)
491{
492 GSM_RingNoteScale DefNoteScale= Scale_880;
493 GSM_RingNoteDuration DefNoteDuration= Duration_1_4;
494 GSM_RingNoteStyle DefNoteStyle= NaturalStyle;
495 int DefNoteTempo= 63, i=0;
496
497 unsigned char buffer[2000],Name[100];
498 GSM_RingNote *Note;
499
500 fread(buffer, 2000, 1, file);
501
502 ringtone->NoteTone.NrCommands = 0;
503
504 /* -------------- name ---------------- */
505 while (buffer[i] != ':') {
506 if (buffer[i] == 0x00) return ERR_NONE;
507 i++;
508 }
509 if (i == 0) {
510 /* This is for RTTL ringtones without name. */
511 EncodeUnicode(ringtone->Name,"Gammu",5);
512 } else {
513 memcpy(Name,buffer,i);
514 Name[i] = 0x00;
515 EncodeUnicode(ringtone->Name,Name,strlen(Name));
516 }
517 i++;
518
519 /* --------- section with default ringtone settings ----------- */
520 while(1) {
521 switch (buffer[i]) {
522 case ':':
523 break;
524 case 0x00:
525 return ERR_NONE;
526 case 'd': case 'D':
527 switch (atoi(buffer+i+2)) {
528 case 1: DefNoteDuration = Duration_Full; break;
529 case 2: DefNoteDuration = Duration_1_2 ; break;
530 case 4: DefNoteDuration = Duration_1_4 ; break;
531 case 8: DefNoteDuration = Duration_1_8 ; break;
532 case 16: DefNoteDuration = Duration_1_16; break;
533 case 32: DefNoteDuration = Duration_1_32; break;
534 }
535 break;
536 case 'o': case 'O':
537 switch (atoi(buffer+i+2)) {
538 case 4: DefNoteScale = Scale_440 ; break;
539 case 5: DefNoteScale = Scale_880 ; break;
540 case 6: DefNoteScale = Scale_1760; break;
541 case 7: DefNoteScale = Scale_3520; break;
542 }
543 break;
544 case 'b': case 'B':
545 DefNoteTempo=atoi(buffer+i+2);
546 dbgprintf("Tempo = %i\n",DefNoteTempo);
547 break;
548 case 's': case 'S':
549 switch (buffer[i+1]) {
550 case 'C': case 'c': DefNoteStyle=ContinuousStyle;break;
551 case 'N': case 'n': DefNoteStyle=NaturalStyle; break;
552 case 'S': case 's': DefNoteStyle=StaccatoStyle; break;
553 }
554 switch (buffer[i+2]) {
555 case 'C': case 'c': DefNoteStyle=ContinuousStyle;break;
556 case 'N': case 'n': DefNoteStyle=NaturalStyle; break;
557 case 'S': case 's': DefNoteStyle=StaccatoStyle; break;
558 }
559 break;
560 }
561 while (buffer[i] != ':' && buffer[i] != ',') {
562 if (buffer[i] == 0x00) return ERR_NONE;
563 i++;
564 }
565 if (buffer[i] == ',') i++;
566 if (buffer[i] == ':') break;
567 }
568 dbgprintf("DefNoteDuration=%d\n", DefNoteDuration);
569 dbgprintf("DefNoteScale=%d\n", DefNoteScale);
570 i++;
571
572 /* ------------------------- notes ------------------------------ */
573 while (buffer[i] != 0x00 && ringtone->NoteTone.NrCommands != MAX_RINGTONE_NOTES) {
574 switch(buffer[i]) {
575 case 'z': case 'Z':
576 switch (buffer[i+1]) {
577 case 'd':
578 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableLED;
579 ringtone->NoteTone.NrCommands++;
580 break;
581 case 'D':
582 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableLED;
583 ringtone->NoteTone.NrCommands++;
584 break;
585 case 'v':
586 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableVibra;
587 ringtone->NoteTone.NrCommands++;
588 break;
589 case 'V':
590 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableVibra;
591 ringtone->NoteTone.NrCommands++;
592 break;
593 case 'l':
594 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableLight;
595 ringtone->NoteTone.NrCommands++;
596 break;
597 case 'L':
598 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableLight;
599 ringtone->NoteTone.NrCommands++;
600 }
601 break;
602 case 'o': case 'O':
603 switch (buffer[i+2]) {
604 case 4: DefNoteScale = Scale_440 ; break;
605 case 5: DefNoteScale = Scale_880 ; break;
606 case 6: DefNoteScale = Scale_1760; break;
607 case 7: DefNoteScale = Scale_3520; break;
608 }
609 break;
610 case 's': case 'S':
611 switch (buffer[i+1]) {
612 case 'C': case 'c': DefNoteStyle=ContinuousStyle;break;
613 case 'N': case 'n': DefNoteStyle=NaturalStyle; break;
614 case 'S': case 's': DefNoteStyle=StaccatoStyle; break;
615 }
616 switch (buffer[i+2]) {
617 case 'C': case 'c': DefNoteStyle=ContinuousStyle;break;
618 case 'N': case 'n': DefNoteStyle=NaturalStyle; break;
619 case 'S': case 's': DefNoteStyle=StaccatoStyle; break;
620 }
621 break;
622 default:
623 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_Note;
624 Note = &ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Note;
625 Note->Style = DefNoteStyle;
626 Note->Tempo = DefNoteTempo;
627 Note->Scale = DefNoteScale;
628 Note->Duration = DefNoteDuration;
629 Note->DurationSpec = NoSpecialDuration;
630 Note->Note = Note_Pause;
631
632 /* Duration */
633 switch (atoi(buffer+i)) {
634 case 1: Note->Duration = Duration_Full ; break;
635 case 2: Note->Duration = Duration_1_2 ; break;
636 case 4: Note->Duration = Duration_1_4 ; break;
637 case 8: Note->Duration = Duration_1_8 ; break;
638 case 16: Note->Duration = Duration_1_16 ; break;
639 case 32: Note->Duration = Duration_1_32 ; break;
640 }
641 /* We skip all numbers from duration specification */
642 while(isdigit(buffer[i])) i++;
643
644 /* Some files can have special duration here */
645 if (buffer[i]=='.') {
646 Note->DurationSpec = DottedNote;
647 i++;
648 }
649
650 /* Note */
651 /* B or b is not in specs, but I decided to put it, because
652 * it's in some RTTL files. It's the same to H note */
653 switch (buffer[i]) {
654 case 'A': case 'a': Note->Note = Note_A; break;
655 case 'B': case 'b': Note->Note = Note_H; break;
656 case 'C': case 'c': Note->Note = Note_C; break;
657 case 'D': case 'd': Note->Note = Note_D; break;
658 case 'E': case 'e': Note->Note = Note_E; break;
659 case 'F': case 'f': Note->Note = Note_F; break;
660 case 'G': case 'g': Note->Note = Note_G; break;
661 case 'H': case 'h': Note->Note = Note_H; break;
662 }
663 i++;
664
665 if (buffer[i]=='#') {
666 switch (Note->Note) {
667 case Note_A : Note->Note = Note_Ais; break;
668 case Note_C : Note->Note = Note_Cis; break;
669 case Note_D : Note->Note = Note_Dis; break;
670 case Note_F : Note->Note = Note_Fis; break;
671 case Note_G : Note->Note = Note_Gis; break;
672 default : break;
673 }
674 i++;
675 }
676
677 /* Some files can have special duration here */
678 if (buffer[i]=='.') {
679 Note->DurationSpec = DottedNote;
680 i++;
681 }
682
683 /* Scale */
684 if (Note->Note!=Note_Pause && isdigit(buffer[i])) {
685 switch (atoi(buffer+i)) {
686 case 4: Note->Scale = Scale_440 ; break;
687 case 5: Note->Scale = Scale_880 ; break;
688 case 6: Note->Scale = Scale_1760; break;
689 case 7: Note->Scale = Scale_3520; break;
690 }
691 i++;
692 }
693
694 ringtone->NoteTone.NrCommands++;
695 break;
696 }
697 while (buffer[i] != ',') {
698 if (buffer[i] == 0x00) return ERR_NONE;
699 i++;
700 }
701 if (buffer[i] == ',') i++;
702 }
703
704 return ERR_NONE;
705}
706
707static GSM_Error loadott(FILE *file, GSM_Ringtone *ringtone)
708{
709 char Buffer[2000];
710 int i;
711
712 i=fread(Buffer, 1, 2000, file);
713
714 return GSM_DecodeNokiaRTTLRingtone(ringtone, Buffer, i);
715}
716
717static GSM_Error loadcommunicator(FILE *file, GSM_Ringtone *ringtone)
718{
719 char Buffer[4000];
720 int i,j;
721
722 i=fread(Buffer, 1, 4000, file);
723
724 i=0;j=0;
725 while (true) {
726 if (Buffer[j] ==0x00 && Buffer[j+1]==0x02 &&
727 Buffer[j+2]==0x4a && Buffer[j+3]==0x3a) break;
728 if (j==i-4) return ERR_UNKNOWN;
729 j++;
730 }
731 j++;
732
733 return GSM_DecodeNokiaRTTLRingtone(ringtone, Buffer+j, i-j);
734}
735
736static GSM_Error loadbin(FILE *file, GSM_Ringtone *ringtone)
737{
738 int i;
739 unsigned charbuffer[2000];
740
741 dbgprintf("loading binary\n");
742 ringtone->NokiaBinary.Length=fread(buffer, 1, 500, file);
743 i=5;
744 while (buffer[i]!=0x00) i++;
745 EncodeUnicode(ringtone->Name,buffer+5,i-5);
746 while (buffer[i]!=0x02 && buffer[i+1]!=0xFC && buffer[i+2]!=0x09) {
747 i++;
748 }
749 ringtone->NokiaBinary.Length=ringtone->NokiaBinary.Length-i;
750 memcpy(ringtone->NokiaBinary.Frame,buffer+i,ringtone->NokiaBinary.Length);
751 dbgprintf("Length %i name \"%s\"\n",ringtone->NokiaBinary.Length,DecodeUnicodeString(ringtone->Name));
752 return ERR_NONE;
753}
754
755static GSM_Error loadpuremidi(FILE *file, GSM_Ringtone *ringtone)
756{
757 unsigned char buffer[30000];
758
759 dbgprintf("loading midi\n");
760 EncodeUnicode(ringtone->Name,"MIDI",4);
761 ringtone->NokiaBinary.Length=fread(buffer, 1, 30000, file);
762 memcpy(ringtone->NokiaBinary.Frame,buffer,ringtone->NokiaBinary.Length);
763 dbgprintf("Length %i name \"%s\"\n",ringtone->NokiaBinary.Length,DecodeUnicodeString(ringtone->Name));
764 return ERR_NONE;
765}
766
767static GSM_Error loadre(FILE *file, GSM_Ringtone *ringtone)
768{
769 unsigned char buffer[2000];
770
771 ringtone->NokiaBinary.Length=fread(buffer, 1, 500, file);
772
773 if (buffer[18]==0x00 && buffer[21]!=0x02) {
774 /* DCT3, Unicode subformat, 62xx & 7110 */
775 CopyUnicodeString(ringtone->Name,buffer+18);
776 ringtone->NokiaBinary.Length = ringtone->NokiaBinary.Length - (21+UnicodeLength(ringtone->Name)*2);
777 memcpy(ringtone->NokiaBinary.Frame,buffer+21+UnicodeLength(ringtone->Name)*2,ringtone->NokiaBinary.Length);
778 } else {
779 /* DCT3, normal subformat, 32xx/33xx/51xx/5210/5510/61xx/8xxx */
780 EncodeUnicode(ringtone->Name,buffer+17,buffer[16]);
781 ringtone->NokiaBinary.Length = ringtone->NokiaBinary.Length - (19+UnicodeLength(ringtone->Name));
782 memcpy(ringtone->NokiaBinary.Frame,buffer+19+UnicodeLength(ringtone->Name),ringtone->NokiaBinary.Length);
783 }
784 dbgprintf("Name \"%s\"\n",DecodeUnicodeString(ringtone->Name));
785 return ERR_NONE;
786}
787
788GSM_Error GSM_ReadRingtoneFile(char *FileName, GSM_Ringtone *ringtone)
789{
790 FILE *file;
791 unsigned charbuffer[300];
792 GSM_Errorerror = ERR_UNKNOWN;
793
794 dbgprintf("Loading ringtone %s\n",FileName);
795 file = fopen(FileName, "rb");
796 if (file == NULL) return ERR_CANTOPENFILE;
797
798 /* Read the header of the file. */
799 fread(buffer, 1, 4, file);
800 if (ringtone->Format == 0x00) {
801 ringtone->Format = RING_NOTETONE;
802 if (buffer[0]==0x00 && buffer[1]==0x00 &&
803 buffer[2]==0x0C && buffer[3]==0x01) {
804 ringtone->Format = RING_NOKIABINARY;
805 }
806 if (buffer[0]==0x00 && buffer[1]==0x00 &&
807 buffer[2]==0x00) {
808 ringtone->Format = RING_NOKIABINARY;
809 }
810 if (buffer[0]==0x4D && buffer[1]==0x54 &&
811 buffer[2]==0x68 && buffer[3]==0x64) {
812 ringtone->Format = RING_MIDI;
813 }
814 }
815 rewind(file);
816 switch (ringtone->Format) {
817 case RING_NOTETONE:
818 if (buffer[0]==0x02 && buffer[1]==0x4A) {
819 error=loadott(file,ringtone);
820 } else if (buffer[0]==0xC7 && buffer[1]==0x45) {
821 error=loadcommunicator(file,ringtone);
822 } else {
823 error=loadrttl(file,ringtone);
824 }
825 ringtone->NoteTone.AllNotesScale=false;
826 break;
827 case RING_NOKIABINARY:
828 if (buffer[0]==0x00 && buffer[1]==0x00 &&
829 buffer[2]==0x0C && buffer[3]==0x01) {
830 error=loadbin(file,ringtone);
831 }
832 if (buffer[0]==0x00 && buffer[1]==0x00 &&
833 buffer[2]==0x00) {
834 error=loadre(file,ringtone);
835 }
836 break;
837 case RING_MIDI:
838 EncodeUnicode(ringtone->Name,FileName,strlen(FileName));
839 error = loadpuremidi(file,ringtone);
840 }
841 fclose(file);
842 return(error);
843}
844
845/* -------------------------- required with Nokia & RTTL ------------------- */
846
847/* Beats per Minute like written in Smart Messaging */
848static int SM_BeatsPerMinute[] = {
849 25, 28, 31, 35, 40, 45, 50, 56, 63,70,
850 80, 90, 100, 112, 125, 140, 160, 180, 200,225,
851 250, 285, 320, 355, 400, 450, 500, 565, 635,715,
852 800,900
853};
854
855int GSM_RTTLGetTempo(int Beats)
856{
857 int i=0;
858
859 while (Beats > SM_BeatsPerMinute[i] && SM_BeatsPerMinute[i] != 900) i++;
860
861 return i<<3;
862}
863
864/* This function packs the ringtone from the structure "ringtone" to
865 "package", where maxlength means length of package.
866 Function returns number of packed notes and change maxlength to
867 number of used chars in "package" */
868unsigned char GSM_EncodeNokiaRTTLRingtone(GSM_Ringtone ringtone, unsigned char *package, int *maxlength)
869{
870 unsigned char CommandLength = 0x02;
871 unsigned char Loop = 0x15;/* Infinite */
872
873 unsigned char Buffer[200];
874 int StartBit=0, OldStartBit;
875 int StartBitHowManyCommands;
876 int HowManyCommands = 0;/* How many instructions packed */
877 int HowManyNotes= 0;
878 int i,j;
879 bool started;
880 GSM_RingNote *Note;
881
882 GSM_RingNoteScale DefScale = 255;
883 GSM_RingNoteStyle DefStyle = 255;
884 int DefTempo = 255;
885
886 AddBufferByte(package, &StartBit, CommandLength, 8);
887 AddBufferByte(package, &StartBit, SM_Command_RingingToneProgramming, 7);
888
889 /* According to specification we need have next part octet-aligned */
890 BufferAlign(package, &StartBit);
891
892 AddBufferByte(package, &StartBit, SM_Command_Sound, 7);
893 AddBufferByte(package, &StartBit, SM_Song_BasicSongType, 3);
894
895 /* Packing the name of the tune. */
896 EncodeUnicodeSpecialNOKIAChars(Buffer, ringtone.Name, UnicodeLength(ringtone.Name));
897 AddBufferByte(package, &StartBit, ((unsigned char)(UnicodeLength(Buffer)<<4)), 4);
898 AddBuffer(package, &StartBit, DecodeUnicodeString(Buffer), 8*UnicodeLength(Buffer));
899
900 /* Packing info about song pattern */
901 AddBufferByte(package, &StartBit, 0x01, 8); //one pattern
902 AddBufferByte(package, &StartBit, SM_InstructionID_PatternHeaderId, 3);
903 AddBufferByte(package, &StartBit, SM_PatternID_A_part, 2);
904 AddBufferByte(package, &StartBit, ((unsigned char)(Loop<<4)), 4);
905
906 /* Later here will be HowManyCommands */
907 StartBitHowManyCommands=StartBit;
908 StartBit = StartBit + 8;
909
910 started = false;
911 for (i=0; i<ringtone.NoteTone.NrCommands; i++) {
912 if (ringtone.NoteTone.Commands[i].Type != RING_Note) {
913 HowManyNotes++;
914 continue;
915 }
916 Note = &ringtone.NoteTone.Commands[i].Note;
917 if (!started) {
918 /* First note can't be Pause - it makes problems
919 * for example with PC Composer
920 */
921 if (Note->Note != Note_Pause) started = true;
922 }
923 if (!started) {
924 HowManyNotes++;
925 continue;
926 }
927 OldStartBit = StartBit;
928 /* we don't write Scale & Style info before "Pause" note - it saves place */
929 if (Note->Note!=Note_Pause) {
930 if (DefScale != Note->Scale || ringtone.NoteTone.AllNotesScale) {
931 j = StartBit+5+8;
932 BufferAlignNumber(&j);
933 if ((j/8)>(*maxlength)) {
934 StartBit = OldStartBit;
935 break;
936 }
937 DefScale = Note->Scale;
938 AddBufferByte(package, &StartBit, SM_InstructionID_ScaleInstructionId, 3);
939 AddBufferByte(package, &StartBit, ((unsigned char)((DefScale-4)<<6)), 2);
940 HowManyCommands++;
941 }
942 if (DefStyle != Note->Style) {
943 j = StartBit+5+8;
944 BufferAlignNumber(&j);
945 if ((j/8)>(*maxlength)) {
946 StartBit = OldStartBit;
947 break;
948 }
949 DefStyle = Note->Style;
950 AddBufferByte(package, &StartBit, SM_InstructionID_StyleInstructionId, 3);
951 AddBufferByte(package, &StartBit, ((unsigned char)DefStyle), 2);
952 HowManyCommands++;
953 }
954 }
955 /* Beats per minute/tempo of the tune */
956 if (DefTempo != GSM_RTTLGetTempo(Note->Tempo)) {
957 j = StartBit+8+8;
958 BufferAlignNumber(&j);
959 if ((j/8)>(*maxlength)) {
960 StartBit = OldStartBit;
961 break;
962 }
963 DefTempo=GSM_RTTLGetTempo(Note->Tempo);
964 /* Adding beats per minute (tempo) of the tune */
965 AddBufferByte(package, &StartBit, SM_InstructionID_TempoInstructionId, 3);
966 AddBufferByte(package, &StartBit, ((unsigned char)DefTempo), 5);
967 HowManyCommands++;
968 }
969 j = StartBit+12+8;
970 BufferAlignNumber(&j);
971 if ((j/8)>(*maxlength)) {
972 StartBit = OldStartBit;
973 break;
974 }
975 /* Note */
976 AddBufferByte(package, &StartBit, SM_InstructionID_NoteInstructionId, 3);
977 AddBufferByte(package, &StartBit, ((unsigned char)Note->Note), 4);
978 AddBufferByte(package, &StartBit, ((unsigned char)Note->Duration), 3);
979 AddBufferByte(package, &StartBit, ((unsigned char)Note->DurationSpec), 2);
980 HowManyCommands++;
981 /* We are sure, we pack it for SMS or setting to phone, not for OTT file */
982 if (*maxlength<1000) {
983 /* Like Pc Composer say - before of phone limitations...*/
984 if (HowManyNotes==130-1) break;
985 }
986 HowManyNotes++;
987 }
988
989 BufferAlign(package, &StartBit);
990 AddBufferByte(package, &StartBit, SM_CommandEnd_CommandEnd, 8);
991
992 OldStartBit = StartBit;
993 StartBit = StartBitHowManyCommands;
994 /* HowManyCommands */
995 AddBufferByte(package, &StartBit, ((unsigned char)HowManyCommands), 8);
996 StartBit = OldStartBit;
997
998 *maxlength=StartBit/8;
999
1000 return(i);
1001}
1002
1003GSM_Error GSM_DecodeNokiaRTTLRingtone(GSM_Ringtone *ringtone, unsigned char *package, int maxlength)
1004{
1005 int StartBit=0, HowMany, l, q, i, spec;
1006 char Buffer[100];
1007 GSM_RingNote *Note;
1008
1009 /* Default ringtone parameters */
1010 GSM_RingNoteScale DefScale= Scale_880;
1011 GSM_RingNoteStyle DefStyle = NaturalStyle;
1012 int DefTempo= 63;
1013
1014 ringtone->Format = RING_NOTETONE;
1015 ringtone->NoteTone.NrCommands = 0;
1016
1017 GetBufferInt(package,&StartBit,&l,8);
1018 if (l!=0x02) {
1019 dbgprintf("Not header\n");
1020 return ERR_NOTSUPPORTED;
1021 }
1022
1023 GetBufferInt(package,&StartBit,&l,7);
1024 if (l!=SM_Command_RingingToneProgramming) {
1025 dbgprintf("Not RingingToneProgramming\n");
1026 return ERR_NOTSUPPORTED;
1027 }
1028
1029 /* According to specification we need have next part octet-aligned */
1030 BufferAlignNumber(&StartBit);
1031
1032 GetBufferInt(package,&StartBit,&l,7);
1033 if (l!=SM_Command_Sound) {
1034 dbgprintf("Not Sound\n");
1035 return ERR_NOTSUPPORTED;
1036 }
1037
1038 GetBufferInt(package,&StartBit,&l,3);
1039 if (l!=SM_Song_BasicSongType) {
1040 dbgprintf("Not BasicSongType\n");
1041 return ERR_NOTSUPPORTED;
1042 }
1043
1044 /* Getting length of the tune name */
1045 GetBufferInt(package,&StartBit,&l,4);
1046 l=l>>4;
1047
1048 /* Unpacking the name of the tune. */
1049 GetBuffer(package, &StartBit, Buffer, 8*l);
1050 Buffer[l]=0;
1051 EncodeUnicode(ringtone->Name,Buffer,strlen(Buffer));
1052 DecodeUnicodeSpecialNOKIAChars(Buffer, ringtone->Name, UnicodeLength(ringtone->Name));
1053 CopyUnicodeString(ringtone->Name,Buffer);
1054
1055 GetBufferInt(package,&StartBit,&l,8);
1056 dbgprintf("Number of song patterns: %i\n",l);
1057 /* we support only one song pattern */
1058 if (l!=1) return ERR_NOTSUPPORTED;
1059
1060 GetBufferInt(package,&StartBit,&l,3);
1061 if (l!=SM_InstructionID_PatternHeaderId) {
1062 dbgprintf("Not PatternHeaderId\n");
1063 return ERR_NOTSUPPORTED;
1064 }
1065
1066 /* Pattern ID - we ignore it */
1067 StartBit+=2;
1068
1069 GetBufferInt(package,&StartBit,&l,4);
1070 l=l>>4;
1071 dbgprintf("Loop value: %i\n",l);
1072
1073 HowMany=0;
1074 GetBufferInt(package, &StartBit, &HowMany, 8);
1075
1076 for (i=0;i<HowMany;i++) {
1077 GetBufferInt(package,&StartBit,&q,3);
1078 switch (q) {
1079 case SM_InstructionID_VolumeInstructionId:
1080 StartBit+=4;
1081 break;
1082 case SM_InstructionID_StyleInstructionId:
1083 GetBufferInt(package,&StartBit,&l,2);
1084 if (l>=NaturalStyle && l<=StaccatoStyle) DefStyle = l;
1085 break;
1086 case SM_InstructionID_TempoInstructionId:
1087 GetBufferInt(package,&StartBit,&l,5);
1088 DefTempo=SM_BeatsPerMinute[l>>3];
1089 break;
1090 case SM_InstructionID_ScaleInstructionId:
1091 GetBufferInt(package,&StartBit,&l,2);
1092 DefScale=(l>>6)+4;
1093 break;
1094 case SM_InstructionID_NoteInstructionId:
1095 Note = &ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Note;
1096 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_Note;
1097
1098 GetBufferInt(package,&StartBit,&l,4);
1099 Note->Note=Note_Pause;
1100 if (l >= Note_C && l <= Note_H) Note->Note = l;
1101
1102 GetBufferInt(package,&StartBit,&l,3);
1103 if (l >= Duration_Full && l <= Duration_1_32) Note->Duration = l;
1104
1105 GetBufferInt(package,&StartBit,&spec,2);
1106 if (spec >= NoSpecialDuration && spec <= Length_2_3) {
1107 Note->DurationSpec = spec;
1108 }
1109
1110 Note->Scale = DefScale;
1111 Note->Style = DefStyle;
1112 Note->Tempo = DefTempo;
1113 if (ringtone->NoteTone.NrCommands==MAX_RINGTONE_NOTES) break;
1114 ringtone->NoteTone.NrCommands++;
1115 break;
1116 default:
1117 dbgprintf("Unsupported block %i %i\n",q,i);
1118 return ERR_NOTSUPPORTED;
1119 }
1120 }
1121 return ERR_NONE;
1122}
1123
1124static void RTTL2Binary(GSM_Ringtone *dest, GSM_Ringtone *src)
1125{
1126 int current = 0, i, note, lastnote = 0, duration;
1127 GSM_RingNote *Note;
1128 unsigned char end[] = {0x40, 0x7D, 0x40, 0x5C, 0x0A, 0xFE, 0x40,
1129 0x20, 0x40, 0x7D, 0x40, 0x37, 0x0A, 0xFE,
1130 0x0A, 0x0A, 0x40, 0x32, 0x07, 0x0B};
1131
1132 strcpy(dest->NokiaBinary.Frame+current,"\x02\xFC\x09");current=current+3;
1133 dest->NokiaBinary.Frame[current++]=0x00;
1134
1135 /*This command can be used to loop, where 0xLL = 0x01 - 0x10
1136 *0x01=loop once [...] 0x10=loop infinite
1137 *Commented now
1138
1139 dest->NokiaBinary.Frame[current++]=0x05;
1140 dest->NokiaBinary.Frame[current++]=0xLL;
1141 */
1142 strcpy(dest->NokiaBinary.Frame+current,"\x0A\x01");current=current+2;
1143
1144 for (i=0; i<src->NoteTone.NrCommands; i++) {
1145 if (src->NoteTone.Commands[i].Type != RING_Note) continue;
1146
1147 Note = &src->NoteTone.Commands[i].Note;
1148 note = 64; /* Pause */
1149 if (Note->Note!=Note_Pause) {
1150 if (Note->Note >= Note_C && Note->Note <= Note_H) {
1151 note = 113 + Note->Note/16;
1152 }
1153 switch (Note->Scale) {
1154 case Scale_440 : break;
1155 case Scale_880 : note = note + 12; break;
1156 case Scale_1760: note = note + 24;break;
1157 case Scale_3520: note = note + 36; break;
1158 default : break;
1159 }
1160 }
1161
1162 /* In 7110 we have 8 ms long sounds */
1163 duration = 60000 * GSM_RingNoteGetFullDuration(*Note) / Note->Tempo / 256;
1164
1165 switch (Note->Style) {
1166 case StaccatoStyle:
1167 if (duration) {
1168 /* Note needs only one sound */
1169 dest->NokiaBinary.Frame[current++] = note;
1170 dest->NokiaBinary.Frame[current++] = 1;
1171 duration--;
1172 }
1173 note = 0x40; /* The rest is pause */
1174 case NaturalStyle:
1175 if (note != 0x40 && duration) {
1176 dest->NokiaBinary.Frame[current++] = 0x40;
1177 /* There is small pause between notes */
1178 dest->NokiaBinary.Frame[current++] = 1;
1179 duration--;
1180 }
1181 default:
1182 if (note != 0x40 && note == lastnote && duration) {
1183 dest->NokiaBinary.Frame[current++] = 0x40;
1184 /* There is small pause between same notes */
1185 dest->NokiaBinary.Frame[current++] = 1;
1186 duration--;
1187 }
1188 while (duration > 125) {
1189 dest->NokiaBinary.Frame[current++] = note;
1190 dest->NokiaBinary.Frame[current++] = 125;
1191 duration -= 125;
1192 }
1193 dest->NokiaBinary.Frame[current++] = note;
1194 dest->NokiaBinary.Frame[current++] = duration;
1195 }
1196 lastnote = note;
1197 }
1198 for (i = 0; i < (int)sizeof(end); i++) dest->NokiaBinary.Frame[current++] = end[i];
1199 dest->NokiaBinary.Length=current;
1200}
1201
1202static void Binary2RTTL(GSM_Ringtone *dest, GSM_Ringtone *src)
1203{
1204 int i = 3, j, z, NrNotes = 0, repeat = 0, accuracy;
1205 int StartRepeat = 0, EndRepeat, Speed;
1206 unsigned char command,length=0;
1207 int NotesLen[500];
1208 GSM_RingNoteScale NotesScale[500];
1209 GSM_RingNoteNote Notes[500];
1210 int Lengths[6*4];
1211 GSM_RingNoteDurationSpec DurationSpec[6*4];
1212 GSM_RingNoteDuration Duration[6*4];
1213 bool foundlen;
1214 GSM_RingNote *Note;
1215
1216 while (i<src->NokiaBinary.Length) {
1217 command = src->NokiaBinary.Frame[i];
1218 i++;
1219 if (command != 0x06 && command != 0x00 && command != 0x09) {
1220 length = src->NokiaBinary.Frame[i];
1221 i++;
1222 dbgprintf("Block %02x %02x - ",length,command);
1223 } else dbgprintf("Block %02x - ",command);
1224 if (command >= 114 && command <= 161) {
1225 dbgprintf("note\n");
1226 if (command >= 114 && command <= 124) {
1227 NotesScale[NrNotes] = Scale_440; command -= 114;
1228 } else if (command >= 125 && command <= 137) {
1229 NotesScale[NrNotes] = Scale_880; command -= 126;
1230 } else if (command >= 138 && command <= 149) {
1231 NotesScale[NrNotes] = Scale_1760; command -= 138;
1232 } else if (command >= 150 && command <= 161) {
1233 NotesScale[NrNotes] = Scale_3520; command -= 150;
1234 }
1235 switch (command) {
1236 case 0 : Notes[NrNotes] = Note_C;break;
1237 case 1 : Notes[NrNotes] = Note_Cis;break;
1238 case 2 : Notes[NrNotes] = Note_D;break;
1239 case 3 : Notes[NrNotes] = Note_Dis;break;
1240 case 4 : Notes[NrNotes] = Note_E;break;
1241 case 5 : Notes[NrNotes] = Note_F;break;
1242 case 6 : Notes[NrNotes] = Note_Fis;break;
1243 case 7 : Notes[NrNotes] = Note_G;break;
1244 case 8 : Notes[NrNotes] = Note_Gis;break;
1245 case 9 : Notes[NrNotes] = Note_A;break;
1246 case 10 : Notes[NrNotes] = Note_Ais;break;
1247 case 11 : Notes[NrNotes] = Note_H;break;
1248 }
1249 if (NrNotes > 0) {
1250 if (Notes[NrNotes-1] == Notes[NrNotes] &&
1251 NotesScale[NrNotes-1] == NotesScale[NrNotes]) {
1252 NotesLen[NrNotes-1]+=length;
1253 } else {
1254 NotesLen[NrNotes]=length;
1255 NrNotes++;
1256 }
1257 } else {
1258 NotesLen[NrNotes]=length;
1259 NrNotes++;
1260 }
1261 } else switch (command) {
1262 case 0x00:
1263 dbgprintf("Unknown\n");
1264 break;
1265 case 0x05:
1266 dbgprintf("repeat %i times\n",length);
1267 repeat = length;
1268 StartRepeat = NrNotes;
1269 break;
1270 case 0x06:
1271 dbgprintf("end repeat\n");
1272 EndRepeat = NrNotes;
1273 for (z=0;z<repeat-1;z++) {
1274 for (j=StartRepeat;j<EndRepeat;j++) {
1275 Notes[NrNotes] = Notes[j];
1276 NotesScale[NrNotes] = NotesScale[j];
1277 NotesLen[NrNotes] = NotesLen[j];
1278 NrNotes++;
1279 dbgprintf("Adding repeat note %i %i\n",Notes[j],NotesLen[j]);
1280 }
1281 }
1282 break;
1283 case 0x07:
1284 if (length == 0x0B) {
1285 dbgprintf("Ringtone end\n");
1286 i = src->NokiaBinary.Length + 1;
1287 }
1288 break;
1289 case 0x09:
1290 dbgprintf("Unknown\n");
1291 break;
1292 case 0x0A:
1293 if (length == 0x01) {
1294 dbgprintf("Let's start our song\n");
1295 break;
1296 }
1297 if (length == 0x0A) {
1298 dbgprintf("Ending joining note\n");
1299 break;
1300 }
1301 if (length == 0xFE) {
1302 dbgprintf("Starting joining note\n");
1303 break;
1304 }
1305 break;
1306 case 0x40:
1307 dbgprintf("Pause\n");
1308 Notes[NrNotes] = Note_Pause;
1309 if (NrNotes > 0) {
1310 if (Notes[NrNotes-1] == Notes[NrNotes] &&
1311 NotesScale[NrNotes-1] == NotesScale[NrNotes]) {
1312 NotesLen[NrNotes-1]+=length;
1313 } else {
1314 NotesLen[NrNotes]=length;
1315 NrNotes++;
1316 }
1317 } else {
1318 NotesLen[NrNotes]=length;
1319 NrNotes++;
1320 }
1321 break;
1322 default:
1323 dbgprintf("Unknown\n");
1324 }
1325 }
1326
1327 while (NrNotes>0) {
1328 if (Notes[NrNotes-1] == Note_Pause) {
1329 NrNotes--;
1330 } else break;
1331 }
1332
1333 for (accuracy=1; accuracy<5; accuracy++) {
1334 i = 1;
1335 while (i < 1000) {
1336 Lengths[0] = 30000/i;
1337 for (j=0;j<5;j++) Lengths[j+1] = Lengths[j] / 2;
1338 for (j=0;j<6;j++) Lengths[6+j] = Lengths[j] * 3/2;
1339 for (j=0;j<6;j++) Lengths[12+j] = Lengths[j] * 9/4;
1340 for (j=0;j<6;j++) Lengths[18+j] = Lengths[j] * 2/3;
1341
1342#ifdef DEBUG
1343 dbgprintf("Length matrix (%i) : ",i);
1344 for (j=0;j<6*4;j++) dbgprintf("%i ",Lengths[j]);
1345 dbgprintf("\n");
1346#endif
1347 foundlen = false;
1348
1349 for (j=0;j<NrNotes;j++) {
1350 dbgprintf("Comparing to %i\n",NotesLen[j]);
1351 foundlen = false;
1352 for (z=0;z<6*4;z++) {
1353 if (NotesLen[j] - Lengths[z] > -accuracy &&
1354 NotesLen[j] - Lengths[z] < accuracy) {
1355 foundlen = true;
1356 break;
1357 }
1358 }
1359 if (!foundlen) break;
1360 }
1361 if (foundlen) break;
1362 i++;
1363 }
1364
1365 if (foundlen) {
1366 Speed = i;
1367 Duration[5] = Duration_1_32; Duration[4] = Duration_1_16;
1368 Duration[3] = Duration_1_8; Duration[2] = Duration_1_4;
1369 Duration[1] = Duration_1_2; Duration[0] = Duration_Full;
1370 for (i=0;i<6;i++) Duration[i] = Duration[i];
1371 for (i=0;i<6;i++) Duration[i+6] = Duration[i];
1372 for (i=0;i<6;i++) Duration[i+12] = Duration[i];
1373 for (i=0;i<6;i++) Duration[i+18] = Duration[i];
1374 for (i=0;i<6;i++) DurationSpec[i] = NoSpecialDuration;
1375 for (i=0;i<6;i++) DurationSpec[i+6] = DottedNote;
1376 for (i=0;i<6;i++) DurationSpec[i+12] = DoubleDottedNote;
1377 for (i=0;i<6;i++) DurationSpec[i+18] = Length_2_3;
1378
1379 for (i=0;i<NrNotes;i++) {
1380 dest->NoteTone.Commands[i].Type= RING_Note;
1381 Note = &dest->NoteTone.Commands[i].Note;
1382 Note->Note = Notes[i];
1383 Note->Tempo = Speed;
1384 Note->Style = ContinuousStyle;
1385 if (Notes[i] != Note_Pause) Note->Scale = NotesScale[i];
1386 for (z=0;z<6*4;z++) {
1387 if (NotesLen[i] - Lengths[z] > -accuracy &&
1388 NotesLen[i] - Lengths[z] < accuracy) {
1389 Note->Duration = Duration[z];
1390 Note->DurationSpec = DurationSpec[z];
1391 /* Trick from PPM Edit */
1392 if (Note->DurationSpec == DoubleDottedNote) {
1393 switch (Note->Duration) {
1394 case Duration_Full:Note->Duration = Duration_Full;break;
1395 case Duration_1_2 :Note->Duration = Duration_Full;break;
1396 case Duration_1_4 :Note->Duration = Duration_1_2; break;
1397 case Duration_1_8 :Note->Duration = Duration_1_4; break;
1398 case Duration_1_16:Note->Duration = Duration_1_8; break;
1399 case Duration_1_32:Note->Duration = Duration_1_16;break;
1400 }
1401 Note->DurationSpec = NoSpecialDuration;
1402 }
1403 /* Here happy creation */
1404 if (Note->DurationSpec == Length_2_3) {
1405 Note->DurationSpec = NoSpecialDuration;
1406 }
1407
1408 break;
1409 }
1410 }
1411 }
1412 dest->NoteTone.NrCommands = NrNotes;
1413 dbgprintf("speed = %i\n",Speed);
1414 break;
1415 }
1416 }
1417
1418 if (!foundlen) dest->NoteTone.NrCommands = 0;
1419}
1420
1421 GSM_Error GSM_RingtoneConvert(GSM_Ringtone *dest, GSM_Ringtone *src, GSM_RingtoneFormatFormat)
1422{
1423 dest->Format = Format;
1424 CopyUnicodeString(dest->Name,src->Name);
1425 if (src->Format==RING_NOTETONE && Format==RING_NOKIABINARY) {
1426 RTTL2Binary(dest, src);
1427 return ERR_NONE;
1428 }
1429 if (src->Format==RING_NOKIABINARY && Format==RING_NOTETONE) {
1430 Binary2RTTL(dest, src);
1431 return ERR_NONE;
1432 }
1433 /* The same source and target format */
1434 if (src->Format==Format) {
1435 memcpy(dest,src,sizeof(GSM_Ringtone));
1436 return ERR_NONE;
1437 }
1438 return ERR_NOTIMPLEMENTED;
1439}
1440
1441/* 0 = No header and footer, 0.5 = partial header and footer,
1442 * 1.0 = IMelody 1.0, 1.2 = IMelody 1.2 */
1443unsigned char GSM_EncodeEMSSound(GSM_Ringtone ringtone, unsigned char *package, int *maxlength, double version, bool start)
1444{
1445 int i, NrNotes = 0, Len, Max = *maxlength;
1446
1447 GSM_RingNote *Note;
1448
1449 GSM_RingNoteScaleDefNoteScale;
1450 GSM_RingNoteStyleDefNoteStyle=0;
1451 int DefNoteTempo=0;
1452
1453 bool started = false, end;
1454
1455 *maxlength = 0;
1456
1457 if (start) {
1458 if (version != 0) *maxlength+=sprintf(package,"BEGIN:IMELODY%c%c",13,10);
1459 if (version == 1.0) *maxlength+=sprintf(package+(*maxlength),"VERSION:1.0%c%c",13,10);
1460 if (version == 1.2) *maxlength+=sprintf(package+(*maxlength),"VERSION:1.2%c%c",13,10);
1461 if (version >= 1.0) *maxlength+=sprintf(package+(*maxlength),"FORMAT:CLASS1.0%c%c",13,10);
1462 if (version == 1.2) *maxlength+=sprintf(package+(*maxlength),"NAME:%s%c%c",DecodeUnicodeString(ringtone.Name),13,10);
1463 }
1464
1465 DefNoteScale = Scale_880; /* by iMelody definition */
1466
1467 for (i=0;i<ringtone.NoteTone.NrCommands;i++) {
1468 Len = *maxlength;
1469 if (ringtone.NoteTone.Commands[i].Type != RING_Note) continue;
1470
1471 Note = &ringtone.NoteTone.Commands[i].Note;
1472 if (Note->Note == Note_Pause) continue;
1473
1474 if (version == 1.2 && start) {
1475 /* Save the default tempo */
1476 DefNoteTempo = Note->Tempo;
1477 Len+=sprintf(package+Len,"BEAT:%i%c%c",DefNoteTempo,13,10);
1478 dbgprintf("DefNoteTempo=%d\n",DefNoteTempo);
1479
1480 /* Save default style */
1481 DefNoteStyle = Note->Style;
1482 switch (DefNoteStyle) {
1483 case NaturalStyle :Len+=sprintf(package+Len,"STYLE:S0%c%c",13,10); break;
1484 case ContinuousStyle:Len+=sprintf(package+Len,"STYLE:S1%c%c",13,10); break;
1485 case StaccatoStyle :Len+=sprintf(package+Len,"STYLE:S2%c%c",13,10); break;
1486 }
1487 }
1488 Len+=sprintf(package+Len,"MELODY:");
1489 if (version != 0) {
1490 /* 15 = Len of END:IMELODY... */
1491 if ((Len+15) > Max) { end = true; break; }
1492 } else {
1493 if (Len > Max) { end = true; break; }
1494 }
1495 *maxlength = Len;
1496 break;
1497 }
1498
1499 for (i=0;i<ringtone.NoteTone.NrCommands;i++) {
1500 end = false;
1501 Len = *maxlength;
1502 switch (ringtone.NoteTone.Commands[i].Type) {
1503 case RING_Note:
1504 Note = &ringtone.NoteTone.Commands[i].Note;
1505 if (!started && Note->Note != Note_Pause) started = true;
1506 if (!started) break;
1507 if (Note->Note!=Note_Pause && Note->Scale != DefNoteScale) {
1508 Len+=sprintf(package+Len,"*%i",Note->Scale-1);
1509 }
1510 switch (Note->Note) {
1511 case Note_C :Len+=sprintf(package+Len,"c");break;
1512 case Note_Cis:Len+=sprintf(package+Len,"#c");break;
1513 case Note_D :Len+=sprintf(package+Len,"d");break;
1514 case Note_Dis:Len+=sprintf(package+Len,"#d");break;
1515 case Note_E :Len+=sprintf(package+Len,"e");break;
1516 case Note_F :Len+=sprintf(package+Len,"f");break;
1517 case Note_Fis:Len+=sprintf(package+Len,"#f");break;
1518 case Note_G :Len+=sprintf(package+Len,"g");break;
1519 case Note_Gis:Len+=sprintf(package+Len,"#g");break;
1520 case Note_A :Len+=sprintf(package+Len,"a");break;
1521 case Note_Ais:Len+=sprintf(package+Len,"#a");break;
1522 case Note_H :Len+=sprintf(package+Len,"b");break;
1523 case Note_Pause :Len+=sprintf(package+Len,"r");break;
1524 }
1525 switch (Note->Duration) {
1526 case Duration_Full : package[Len++]='0';break;
1527 case Duration_1_2 : package[Len++]='1';break;
1528 case Duration_1_4 : package[Len++]='2';break;
1529 case Duration_1_8 : package[Len++]='3';break;
1530 case Duration_1_16 : package[Len++]='4';break;
1531 case Duration_1_32 : package[Len++]='5';break;
1532 default : break;
1533 }
1534 switch (Note->DurationSpec) {
1535 case DottedNote : package[Len++] = '.'; break;
1536 case DoubleDottedNote: package[Len++] = ':'; break;
1537 case Length_2_3 : package[Len++] = ';'; break;
1538 default : break;
1539 }
1540 if (version != 0) {
1541 /* 15 = Len of END:IMELODY... */
1542 if ((Len+15) > Max) { end = true; break; }
1543 } else {
1544 if (Len > Max) { end = true; break; }
1545 }
1546 *maxlength = Len;
1547 break;
1548 case RING_DisableLED:
1549 if ((Len + 6) > Max) { end = true; break; }
1550 (*maxlength)+=sprintf(package+Len,"ledoff");
1551 break;
1552 case RING_EnableLED:
1553 if ((Len + 5) > Max) { end = true; break; }
1554 (*maxlength)+=sprintf(package+Len,"ledon");
1555 break;
1556 case RING_DisableVibra:
1557 if ((Len + 7) > Max) { end = true; break; }
1558 (*maxlength)+=sprintf(package+Len,"vibeoff");
1559 break;
1560 case RING_EnableVibra:
1561 if ((Len + 6) > Max) { end = true; break; }
1562 (*maxlength)+=sprintf(package+Len,"vibeon");
1563 break;
1564 case RING_DisableLight:
1565 if ((Len + 7) > Max) { end = true; break; }
1566 (*maxlength)+=sprintf(package+Len,"backoff");
1567 break;
1568 case RING_EnableLight:
1569 if ((Len + 6) > Max) { end = true; break; }
1570 (*maxlength)+=sprintf(package+Len,"backon");
1571 break;
1572 default:
1573 break;
1574 }
1575 if (end) break;
1576 NrNotes ++;
1577 }
1578
1579 if (version != 0) *maxlength+=sprintf(package+(*maxlength),"%c%cEND:IMELODY%c%c",13,10,13,10);
1580
1581 return NrNotes;
1582}
1583
1584char *GSM_GetRingtoneName(GSM_AllRingtonesInfo *Info, int ID)
1585{
1586 int i;
1587 static char ala[2];
1588
1589 for (i=0;i<Info->Number;i++) {
1590 if (Info->Ringtone[i].ID == ID) return Info->Ringtone[i].Name;
1591 }
1592
1593 ala[0] = 0;
1594 ala[1] = 0;
1595 return ala;
1596}
1597
1598/* How should editor hadle tabs in this file? Add editor commands here.
1599 * vim: noexpandtab sw=8 ts=8 sts=8:
1600 */