Imported gammu 0.90.7
[gammu.git] / common / service / gsmring.c
blob85d2710df6ef9f4ec118c7ec2b1f1d0d7e332eff
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <math.h>
6 #ifdef WIN32
7 # include <windows.h>
8 #endif
10 #include "../gsmcomon.h"
11 #include "../misc/coding/coding.h"
12 #include "../gsmstate.h"
13 #include "gsmring.h"
14 #include "sms/gsmsms.h"
16 int GSM_RingNoteGetFrequency(GSM_RingNote Note)
18 double freq=0;
20 /* Values according to the software from http://iki.fi/too/sw/xring/
21 * generated with:
22 * perl -e 'print int(4400 * (2 **($_/12)) + .5)/10, "\n" for(3..14)'
24 switch (Note.Note) {
25 case Note_C : freq = 523.3; break;
26 case Note_Cis: freq = 554.4; break;
27 case Note_D : freq = 587.3; break;
28 case Note_Dis: freq = 622.3; break;
29 case Note_E : freq = 659.3; break;
30 case Note_F : freq = 698.5; break;
31 case Note_Fis: freq = 740; break;
32 case Note_G : freq = 784; break;
33 case Note_Gis: freq = 830.6; break;
34 case Note_A : freq = 880; break;
35 case Note_Ais: freq = 932.3; break;
36 case Note_H : freq = 987.8; break;
37 case Note_Pause: break;
39 switch (Note.Scale) {
40 case Scale_440 : freq = freq / 2; break;
41 case Scale_880 : break;
42 case Scale_1760: freq = freq * 2; break;
43 case Scale_3520: freq = freq * 4; break;
44 default : break;
46 return (int)freq;
49 int GSM_RingNoteGetFullDuration(GSM_RingNote Note)
51 int duration = 1;
53 switch (Note.Duration) {
54 case Duration_Full : duration = 128; break;
55 case Duration_1_2 : duration = 64; break;
56 case Duration_1_4 : duration = 32; break;
57 case Duration_1_8 : duration = 16; break;
58 case Duration_1_16 : duration = 8; break;
59 case Duration_1_32 : duration = 4; break;
61 switch (Note.DurationSpec) {
62 case NoSpecialDuration : break;
63 case DottedNote : duration = duration * 3/2; break;
64 case DoubleDottedNote : duration = duration * 9/4; break;
65 case Length_2_3 : duration = duration * 2/3; break;
67 return duration;
70 #ifndef PI
71 # define PI 3.141592654
72 #endif
74 #define WAV_SAMPLE_RATE 44100
76 static GSM_Error savewav(FILE *file, GSM_Ringtone *ringtone)
78 unsigned char WAV_Header[] = {
79 'R','I','F','F',
80 0x00,0x00,0x00,0x00, /* Length */
81 'W','A','V','E'};
82 unsigned char FMT_Header[] = {'f','m','t',' ',
83 0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x44,0xac,
84 0x00,0x00,0x88,0x58,0x01,0x00,0x02,0x00,0x10,0x00};
85 unsigned char DATA_Header[] = {
86 'd','a','t','a',
87 0x00,0x00,0x00,0x00}; /* Length */
88 short DATA_Buffer[60000];
89 long wavfilesize;
90 GSM_RingNote *Note;
91 long i,j,length=0;
92 double phase=0,phase_step;
94 fwrite(&WAV_Header, 1, sizeof(WAV_Header), file);
95 fwrite(&FMT_Header, 1, sizeof(FMT_Header), file);
96 fwrite(&DATA_Header, 1, sizeof(DATA_Header), file);
98 for (i=0;i<ringtone->NoteTone.NrCommands;i++) {
99 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
100 Note = &ringtone->NoteTone.Commands[i].Note;
101 phase_step = GSM_RingNoteGetFrequency(*Note)*WAV_SAMPLE_RATE*1.5;
102 for (j=0;j<((long)(GSM_RingNoteGetFullDuration(*Note)*WAV_SAMPLE_RATE/70));j++) {
103 DATA_Buffer[j] = ((int)(sin(phase*PI)*50000));
104 phase = phase + phase_step;
105 length++;
107 fwrite(&DATA_Buffer,sizeof(short),j,file);
111 wavfilesize = sizeof(WAV_Header) + sizeof(FMT_Header) + sizeof(DATA_Header) + length*2;
112 WAV_Header[4] = ((unsigned char)wavfilesize % 256);
113 WAV_Header[5] = ((unsigned char)wavfilesize / 256);
114 WAV_Header[6] = ((unsigned char)wavfilesize / (256*256));
115 WAV_Header[7] = ((unsigned char)wavfilesize / (256*256*256));
116 wavfilesize = wavfilesize - 54;
117 DATA_Header[4] = ((unsigned char)wavfilesize % 256);
118 DATA_Header[5] = ((unsigned char)wavfilesize / 256);
119 DATA_Header[6] = ((unsigned char)wavfilesize / (256*256));
120 DATA_Header[7] = ((unsigned char)wavfilesize / (256*256*256));
122 fseek( file, 0, SEEK_SET);
123 fwrite(&WAV_Header, 1, sizeof(WAV_Header), file);
124 fwrite(&FMT_Header, 1, sizeof(FMT_Header), file);
125 fwrite(&DATA_Header, 1, sizeof(DATA_Header), file);
127 return GE_NONE;
130 static GSM_Error savebin(FILE *file, GSM_Ringtone *ringtone)
132 char nullchar=0x00;
134 fwrite(&nullchar,1,1,file);
135 fwrite(&nullchar,1,1,file);
136 fprintf(file,"\x0C\x01\x2C");
137 fprintf(file,"%s",DecodeUnicodeString(ringtone->Name));
138 fwrite(&nullchar,1,1,file);
139 fwrite(&nullchar,1,1,file);
140 fwrite(ringtone->NokiaBinary.Frame,1,ringtone->NokiaBinary.Length,file);
141 return GE_NONE;
144 static GSM_Error savepuremidi(FILE *file, GSM_Ringtone *ringtone)
146 fwrite(ringtone->NokiaBinary.Frame,1,ringtone->NokiaBinary.Length,file);
147 return GE_NONE;
150 GSM_Error saverttl(FILE *file, GSM_Ringtone *ringtone)
152 GSM_RingNoteScale DefNoteScale;
153 GSM_RingNoteDuration DefNoteDuration;
155 GSM_RingNoteStyle DefNoteStyle=0;
156 int DefNoteTempo=0;
158 bool started = false, firstcomma = true;
159 GSM_RingNote *Note;
161 unsigned char buffer[15];
162 int i,j,k=0;
164 /* Saves ringtone name */
165 fprintf(file,"%s:",DecodeUnicodeString(ringtone->Name));
167 /* Find the most frequently used duration */
168 for (i=0;i<6;i++) buffer[i]=0;
169 for (i=0;i<ringtone->NoteTone.NrCommands;i++) {
170 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
171 Note = &ringtone->NoteTone.Commands[i].Note;
172 /* some durations need 2 bytes in file, some 1 */
173 if (Note->Duration >= Duration_Full && Note->Duration <= Duration_1_8) {
174 buffer[Note->Duration/32]++;
176 if (Note->Duration >= Duration_1_16 && Note->Duration <= Duration_1_32) {
177 buffer[Note->Duration/32]+=2;
181 /* Now find the most frequently used */
182 j=0;
183 for (i=0;i<6;i++) {
184 if (buffer[i]>j) {
185 k=i;
186 j=buffer[i];
189 /* Finally convert the default duration */
190 DefNoteDuration = k * 32;
191 dbgprintf("DefNoteDuration=%d\n", DefNoteDuration);
192 switch (DefNoteDuration) {
193 case Duration_Full:fprintf(file,"d=1"); break;
194 case Duration_1_2 :fprintf(file,"d=2"); break;
195 case Duration_1_4 :fprintf(file,"d=4"); break;
196 case Duration_1_8 :fprintf(file,"d=8"); break;
197 case Duration_1_16:fprintf(file,"d=16");break;
198 case Duration_1_32:fprintf(file,"d=32");break;
201 /* Find the most frequently used scale */
202 for (i=0;i<9;i++) buffer[i]=0;
203 for (i=0;i<ringtone->NoteTone.NrCommands;i++) {
204 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
205 Note = &ringtone->NoteTone.Commands[i].Note;
206 if (Note->Note!=Note_Pause &&
207 Note->Scale >= Scale_55 && Note->Scale <= Scale_14080) {
208 buffer[Note->Scale - 1]++;
212 j=0;
213 for (i=0;i<9;i++) {
214 if (buffer[i]>j) {
215 k = i;
216 j=buffer[i];
219 DefNoteScale = k + 1;
220 /* Save the default scale */
221 fprintf(file,",o=%i,",DefNoteScale);
222 dbgprintf("DefNoteScale=%d\n", DefNoteScale);
224 for (i=0;i<ringtone->NoteTone.NrCommands;i++) {
225 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
226 Note = &ringtone->NoteTone.Commands[i].Note;
228 /* Trick from PPM Edit */
229 if (Note->DurationSpec == DoubleDottedNote) {
230 switch (Note->Duration) {
231 case Duration_Full:Note->Duration = Duration_Full;break;
232 case Duration_1_2 :Note->Duration = Duration_Full;break;
233 case Duration_1_4 :Note->Duration = Duration_1_2; break;
234 case Duration_1_8 :Note->Duration = Duration_1_4; break;
235 case Duration_1_16:Note->Duration = Duration_1_8; break;
236 case Duration_1_32:Note->Duration = Duration_1_16;break;
238 Note->DurationSpec = NoSpecialDuration;
241 if (!started) {
242 DefNoteTempo=Note->Tempo;
243 DefNoteStyle=Note->Style;
244 switch (Note->Style) {
245 case StaccatoStyle : fprintf(file,"s=S,"); break;
246 case NaturalStyle : fprintf(file,"s=N,"); break;
247 case ContinuousStyle : break;
249 /* Save the default tempo */
250 fprintf(file,"b=%i:",DefNoteTempo);
251 dbgprintf("DefNoteTempo=%d\n", DefNoteTempo);
252 started = true;
253 firstcomma = true;
255 if (started) {
256 if (Note->Style!=DefNoteStyle) {
257 /* And a separator */
258 if (!firstcomma) fprintf(file,",");
259 firstcomma = false;
260 DefNoteStyle=Note->Style;
261 switch (Note->Style) {
262 case StaccatoStyle : fprintf(file,"s=S"); break;
263 case NaturalStyle : fprintf(file,"s=N"); break;
264 case ContinuousStyle: fprintf(file,"s=C"); break;
267 if (Note->Tempo!=DefNoteTempo) {
268 /* And a separator */
269 if (!firstcomma) fprintf(file,",");
270 firstcomma = false;
271 DefNoteTempo=Note->Tempo;
272 fprintf(file,"b=%i",DefNoteTempo);
274 /* This note has a duration different than the default. We must save it */
275 if (Note->Duration!=DefNoteDuration) {
276 /* And a separator */
277 if (!firstcomma) fprintf(file,",");
278 firstcomma = false;
279 switch (Note->Duration) {
280 case Duration_Full:fprintf(file,"1"); break;
281 case Duration_1_2 :fprintf(file,"2"); break;
282 case Duration_1_4 :fprintf(file,"4"); break;
283 case Duration_1_8 :fprintf(file,"8"); break;
284 case Duration_1_16:fprintf(file,"16");break;
285 case Duration_1_32:fprintf(file,"32");break;
287 } else {
288 /* And a separator */
289 if (!firstcomma) fprintf(file,",");
290 firstcomma = false;
292 /* Now save the actual note */
293 switch (Note->Note) {
294 case Note_C :fprintf(file,"c"); break;
295 case Note_Cis:fprintf(file,"c#"); break;
296 case Note_D :fprintf(file,"d"); break;
297 case Note_Dis:fprintf(file,"d#"); break;
298 case Note_E :fprintf(file,"e"); break;
299 case Note_F :fprintf(file,"f"); break;
300 case Note_Fis:fprintf(file,"f#"); break;
301 case Note_G :fprintf(file,"g"); break;
302 case Note_Gis:fprintf(file,"g#"); break;
303 case Note_A :fprintf(file,"a"); break;
304 case Note_Ais:fprintf(file,"a#"); break;
305 case Note_H :fprintf(file,"h"); break;
306 default :fprintf(file,"p"); break; /*Pause ?*/
308 switch (Note->DurationSpec) {
309 case DottedNote : fprintf(file,"."); break;
310 default : break;
312 if (Note->Note!=Note_Pause && Note->Scale != DefNoteScale) {
313 fprintf(file,"%i",Note->Scale);
318 return GE_NONE;
321 static void saveimelody(FILE *file, GSM_Ringtone *ringtone)
323 char Buffer[2000];
324 int i=2000;
326 GSM_EncodeEMSSound(*ringtone, Buffer, &i, (float)1.2, true);
328 fwrite(Buffer, 1, i, file);
331 static void WriteVarLen(unsigned char* midifile, int* current, long value)
333 long buffer;
335 buffer = value & 0x7f;
337 while (value >>= 7) {
338 buffer <<= 8;
339 buffer |= 0x80;
340 buffer += (value & 0x7f);
343 while (1) {
344 midifile[(*current)++] = (unsigned char)buffer;
345 if (buffer & 0x80)
346 buffer >>= 8;
347 else
348 break;
352 #define singlepauses
354 /* FIXME: need adding tempo before each note and scale too ? */
355 static void savemid(FILE* file, GSM_Ringtone *ringtone)
357 int pause = 0, current = 26, duration, i, note=0, length = 20;
358 bool started = false;
359 GSM_RingNote *Note;
360 unsigned char midifile[3000] = {
361 0x4D, 0x54, 0x68, 0x64, // MThd
362 0x00, 0x00, 0x00, 0x06, // chunk length
363 0x00, 0x00, // format 0
364 0x00, 0x01, // one track
365 0x00, 0x20, // 32 per quarter note
366 0x4D, 0x54, 0x72, 0x6B, // MTrk
367 0x00, 0x00, 0x00, 0x00, // chunk length
368 0x00, 0xFF, 0x51, 0x03, // tempo meta event
369 0x00, 0x00, 0x00}; // 3 bytes for us for a quarter note
371 for (i = 0; i < ringtone->NoteTone.NrCommands; i++) {
372 if (ringtone->NoteTone.Commands[i].Type == RING_Note) {
373 Note = &ringtone->NoteTone.Commands[i].Note;
374 if (!started) {
375 /* readmid does not read pauses at the beginning */
376 if (Note->Note != Note_Pause) {
377 /* FIXME: we need add tempo before each note or so... */
378 long duration=60000000/Note->Tempo;
380 midifile[current++] = (unsigned char)(duration >> 16);
381 midifile[current++] = (unsigned char)(duration >> 8);
382 midifile[current++] = (unsigned char)duration;
384 started = true;
387 if (started) {
388 duration = GSM_RingNoteGetFullDuration(*Note);
389 if (Note->Note == Note_Pause) {
390 pause += duration;
391 #ifdef singlepauses
392 WriteVarLen(midifile,&current,pause);
393 pause=0;
394 midifile[current++]=0x00; // pause
395 midifile[current++]=0x00;
396 #endif
397 } else {
398 if (Note->Note >= Note_C && Note->Note <= Note_H) {
399 note = Note->Note/16 + 12 * Note->Scale - 1;
402 WriteVarLen(midifile,&current,pause);
403 pause=0;
404 midifile[current++]=0x90; // note on
405 midifile[current++]=note;
406 midifile[current++]=0x64; // forte
408 WriteVarLen(midifile,&current,duration);
409 midifile[current++]=0x80; // note off
410 midifile[current++]=note;
411 midifile[current++]=0x64;
416 if (pause) {
417 WriteVarLen(midifile,&current,pause);
418 midifile[current++]=0x00; // pause
419 midifile[current++]=0x00; //
421 midifile[current++] = 0x00;
422 midifile[current++] = 0xFF; // track end
423 midifile[current++] = 0x2F;
424 midifile[current++] = 0x00;
425 midifile[length++] = (current-22) >> 8;
426 midifile[length++] = current-22;
428 fwrite(midifile,1,current,file);
431 static void saveott(FILE *file, GSM_Ringtone *ringtone)
433 char Buffer[2000];
434 int i=2000;
436 GSM_EncodeNokiaRTTLRingtone(*ringtone, Buffer, &i);
438 fwrite(Buffer, 1, i, file);
441 GSM_Error GSM_SaveRingtoneFile(char *FileName, GSM_Ringtone *ringtone)
443 FILE *file;
445 file = fopen(FileName, "wb");
446 if (file == NULL) return(GE_CANTOPENFILE);
448 switch (ringtone->Format) {
449 case RING_NOTETONE:
450 if (strstr(FileName,".mid")) {
451 savemid(file,ringtone);
452 } else if (strstr(FileName,".ott")) {
453 saveott(file,ringtone);
454 } else if (strstr(FileName,".rng")) {
455 saveott(file,ringtone);
456 } else if (strstr(FileName,".imy")) {
457 saveimelody(file,ringtone);
458 } else if (strstr(FileName,".ime")) {
459 saveimelody(file,ringtone);
460 } else if (strstr(FileName,".wav")) {
461 savewav(file,ringtone);
462 } else {
463 saverttl(file, ringtone);
465 break;
466 case RING_NOKIABINARY:
467 savebin(file, ringtone);
468 break;
469 case RING_MIDI:
470 savepuremidi(file, ringtone);
471 break;
474 fclose(file);
476 return GE_NONE;
479 /* Defines the character that separates fields in rtttl files. */
480 #define RTTTL_SEP ":"
482 static GSM_Error loadrttl(FILE *file, GSM_Ringtone *ringtone)
484 GSM_RingNoteScale DefNoteScale = Scale_880;
485 GSM_RingNoteDuration DefNoteDuration = Duration_1_4;
486 GSM_RingNoteStyle DefNoteStyle = NaturalStyle;
487 int DefNoteTempo = 63;
489 unsigned char buffer[2000],Name[100];
490 unsigned char *def, *notes, *ptr;
491 GSM_RingNote *Note;
493 fread(buffer, 2000, 1, file);
495 ringtone->NoteTone.NrCommands = 0;
497 /* This is for RTTL ringtones without name. */
498 if (buffer[0] != RTTTL_SEP[0]) {
499 strtok(buffer, RTTTL_SEP);
500 sprintf(Name, "%s", buffer);
501 EncodeUnicode(ringtone->Name,Name,strlen(Name));
502 def=strtok(NULL, RTTTL_SEP);
503 notes=strtok(NULL, RTTTL_SEP);
504 } else {
505 EncodeUnicode(ringtone->Name,"Gammu",5);
506 def=strtok(buffer, RTTTL_SEP);
507 notes=strtok(NULL, RTTTL_SEP);
509 ptr=strtok(def, ", ");
510 /* Parsing the <defaults> section. */
511 while (ptr) {
512 switch(*ptr) {
513 case 'd': case 'D':
514 switch (atoi(ptr+2)) {
515 case 1: DefNoteDuration = Duration_Full; break;
516 case 2: DefNoteDuration = Duration_1_2 ; break;
517 case 4: DefNoteDuration = Duration_1_4 ; break;
518 case 8: DefNoteDuration = Duration_1_8 ; break;
519 case 16: DefNoteDuration = Duration_1_16; break;
520 case 32: DefNoteDuration = Duration_1_32; break;
522 break;
523 case 'o': case 'O':
524 switch (atoi(ptr+2)) {
525 case 4: DefNoteScale = Scale_440 ; break;
526 case 5: DefNoteScale = Scale_880 ; break;
527 case 6: DefNoteScale = Scale_1760; break;
528 case 7: DefNoteScale = Scale_3520; break;
530 break;
531 case 'b': case 'B':
532 DefNoteTempo=atoi(ptr+2);
533 dbgprintf("Tempo = %i\n",DefNoteTempo);
534 break;
535 case 's': case 'S':
536 switch (*(ptr+1)) {
537 case 'C': case 'c': DefNoteStyle=ContinuousStyle; break;
538 case 'N': case 'n': DefNoteStyle=NaturalStyle; break;
539 case 'S': case 's': DefNoteStyle=StaccatoStyle; break;
541 switch (*(ptr+2)) {
542 case 'C': case 'c': DefNoteStyle=ContinuousStyle; break;
543 case 'N': case 'n': DefNoteStyle=NaturalStyle; break;
544 case 'S': case 's': DefNoteStyle=StaccatoStyle; break;
546 break;
548 ptr=strtok(NULL,", ");
550 dbgprintf("DefNoteDuration=%d\n", DefNoteDuration);
551 dbgprintf("DefNoteScale=%d\n", DefNoteScale);
552 ptr=strtok(notes, ", ");
553 /* Parsing the <note-command>+ section. */
554 while (ptr && ringtone->NoteTone.NrCommands<MAX_RINGTONE_NOTES) {
555 switch(*ptr) {
556 case 'z': case 'Z':
557 switch (*(ptr+1)) {
558 case 'd':
559 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableLED;
560 ringtone->NoteTone.NrCommands++;
561 break;
562 case 'D':
563 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableLED;
564 ringtone->NoteTone.NrCommands++;
565 break;
566 case 'v':
567 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableVibra;
568 ringtone->NoteTone.NrCommands++;
569 break;
570 case 'V':
571 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableVibra;
572 ringtone->NoteTone.NrCommands++;
573 break;
574 case 'l':
575 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableLight;
576 ringtone->NoteTone.NrCommands++;
577 break;
578 case 'L':
579 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableLight;
580 ringtone->NoteTone.NrCommands++;
582 break;
583 case 'o': case 'O':
584 switch (atoi(ptr+2)) {
585 case 4: DefNoteScale = Scale_440 ; break;
586 case 5: DefNoteScale = Scale_880 ; break;
587 case 6: DefNoteScale = Scale_1760; break;
588 case 7: DefNoteScale = Scale_3520; break;
590 case 's': case 'S':
591 switch (*(ptr+1)) {
592 case 'C': case 'c': DefNoteStyle=ContinuousStyle; break;
593 case 'N': case 'n': DefNoteStyle=NaturalStyle; break;
594 case 'S': case 's': DefNoteStyle=StaccatoStyle; break;
596 switch (*(ptr+2)) {
597 case 'C': case 'c': DefNoteStyle=ContinuousStyle; break;
598 case 'N': case 'n': DefNoteStyle=NaturalStyle; break;
599 case 'S': case 's': DefNoteStyle=StaccatoStyle; break;
601 break;
602 default:
603 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_Note;
604 Note = &ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Note;
605 Note->Style = DefNoteStyle;
606 Note->Tempo = DefNoteTempo;
607 Note->Scale = DefNoteScale;
608 Note->Duration = DefNoteDuration;
609 Note->DurationSpec = NoSpecialDuration;
610 Note->Note = Note_Pause;
612 /* [<duration>] */
613 switch (atoi(ptr)) {
614 case 1: Note->Duration = Duration_Full ; break;
615 case 2: Note->Duration = Duration_1_2 ; break;
616 case 4: Note->Duration = Duration_1_4 ; break;
617 case 8: Note->Duration = Duration_1_8 ; break;
618 case 16: Note->Duration = Duration_1_16 ; break;
619 case 32: Note->Duration = Duration_1_32 ; break;
621 /* Skip all numbers in duration specification. */
622 while(isdigit(*ptr)) ptr++;
624 /* Check for dodgy rttl */
625 /* [<special-duration>] */
626 if (*ptr=='.') {
627 Note->DurationSpec = DottedNote;
628 ptr++;
631 /* <note> */
632 /* B or b is not in specs, but I decided to put it, because
633 * it's in some RTTL files. It's the same to H note */
634 switch (*ptr) {
635 case 'A': case 'a': Note->Note = Note_A; break;
636 case 'B': case 'b': Note->Note = Note_H; break;
637 case 'C': case 'c': Note->Note = Note_C; break;
638 case 'D': case 'd': Note->Note = Note_D; break;
639 case 'E': case 'e': Note->Note = Note_E; break;
640 case 'F': case 'f': Note->Note = Note_F; break;
641 case 'G': case 'g': Note->Note = Note_G; break;
642 case 'H': case 'h': Note->Note = Note_H; break;
644 ptr++;
646 if ((*ptr)=='#') {
647 switch (Note->Note) {
648 case Note_A : Note->Note = Note_Ais; break;
649 case Note_C : Note->Note = Note_Cis; break;
650 case Note_D : Note->Note = Note_Dis; break;
651 case Note_F : Note->Note = Note_Fis; break;
652 case Note_G : Note->Note = Note_Gis; break;
653 default : break;
655 ptr++;
658 /* Check for dodgy rttl */
659 /* [<special-duration>] */
660 if (*ptr=='.') {
661 Note->DurationSpec = DottedNote;
662 ptr++;
665 /* [<scale>] */
666 if (Note->Note!=Note_Pause) {
667 if (isdigit(*ptr)) {
668 switch (atoi(ptr)) {
669 case 4: Note->Scale = Scale_440 ; break;
670 case 5: Note->Scale = Scale_880 ; break;
671 case 6: Note->Scale = Scale_1760; break;
672 case 7: Note->Scale = Scale_3520; break;
674 ptr++;
678 ringtone->NoteTone.NrCommands++;
679 break;
681 ptr=strtok(NULL, ", ");
683 return GE_NONE;
686 static GSM_Error loadott(FILE *file, GSM_Ringtone *ringtone)
688 char Buffer[2000];
689 int i;
691 i=fread(Buffer, 1, 2000, file);
693 return GSM_DecodeNokiaRTTLRingtone(ringtone, Buffer, i);
696 static GSM_Error loadcommunicator(FILE *file, GSM_Ringtone *ringtone)
698 char Buffer[4000];
699 int i,j;
701 i=fread(Buffer, 1, 4000, file);
703 i=0;j=0;
704 while (true) {
705 if (Buffer[j] ==0x00 && Buffer[j+1]==0x02 &&
706 Buffer[j+2]==0x4a && Buffer[j+3]==0x3a) break;
707 if (j==i-4) return GE_UNKNOWN;
708 j++;
710 j++;
712 return GSM_DecodeNokiaRTTLRingtone(ringtone, Buffer+j, i-j);
715 static GSM_Error loadbin(FILE *file, GSM_Ringtone *ringtone)
717 int i;
718 unsigned char buffer[2000];
720 dbgprintf("loading binary\n");
721 ringtone->NokiaBinary.Length=fread(buffer, 1, 500, file);
722 i=5;
723 while (buffer[i]!=0x00) i++;
724 EncodeUnicode(ringtone->Name,buffer+5,i-5);
725 while (buffer[i]!=0x02 && buffer[i+1]!=0xFC && buffer[i+2]!=0x09) {
726 i++;
728 ringtone->NokiaBinary.Length=ringtone->NokiaBinary.Length-i;
729 memcpy(ringtone->NokiaBinary.Frame,buffer+i,ringtone->NokiaBinary.Length);
730 dbgprintf("Length %i name \"%s\"\n",ringtone->NokiaBinary.Length,DecodeUnicodeString(ringtone->Name));
731 return GE_NONE;
734 static GSM_Error loadpuremidi(FILE *file, GSM_Ringtone *ringtone)
736 unsigned char buffer[30000];
738 dbgprintf("loading midi\n");
739 EncodeUnicode(ringtone->Name,"MIDI",4);
740 ringtone->NokiaBinary.Length=fread(buffer, 1, 30000, file);
741 memcpy(ringtone->NokiaBinary.Frame,buffer,ringtone->NokiaBinary.Length);
742 dbgprintf("Length %i name \"%s\"\n",ringtone->NokiaBinary.Length,DecodeUnicodeString(ringtone->Name));
743 return GE_NONE;
746 static GSM_Error loadre(FILE *file, GSM_Ringtone *ringtone)
748 unsigned char buffer[2000];
750 ringtone->NokiaBinary.Length=fread(buffer, 1, 500, file);
752 if (buffer[18]==0x00 && buffer[21]!=0x02) {
753 /* DCT3, Unicode subformat, 62xx & 7110 */
754 CopyUnicodeString(ringtone->Name,buffer+18);
755 ringtone->NokiaBinary.Length = ringtone->NokiaBinary.Length - (21+UnicodeLength(ringtone->Name)*2);
756 memcpy(ringtone->NokiaBinary.Frame,buffer+21+UnicodeLength(ringtone->Name)*2,ringtone->NokiaBinary.Length);
757 } else {
758 /* DCT3, normal subformat, 32xx/33xx/51xx/5210/5510/61xx/8xxx */
759 EncodeUnicode(ringtone->Name,buffer+17,buffer[16]);
760 ringtone->NokiaBinary.Length = ringtone->NokiaBinary.Length - (19+UnicodeLength(ringtone->Name));
761 memcpy(ringtone->NokiaBinary.Frame,buffer+19+UnicodeLength(ringtone->Name),ringtone->NokiaBinary.Length);
763 dbgprintf("Name \"%s\"\n",DecodeUnicodeString(ringtone->Name));
764 return GE_NONE;
767 GSM_Error GSM_ReadRingtoneFile(char *FileName, GSM_Ringtone *ringtone)
769 FILE *file;
770 unsigned char buffer[300];
771 GSM_Error error = GE_UNKNOWN;
773 dbgprintf("Loading ringtone %s\n",FileName);
774 file = fopen(FileName, "rb");
775 if (file == NULL) return(GE_CANTOPENFILE);
777 /* Read the header of the file. */
778 fread(buffer, 1, 4, file);
779 if (ringtone->Format == 0x00) {
780 ringtone->Format = RING_NOTETONE;
781 if (buffer[0]==0x00 && buffer[1]==0x00 &&
782 buffer[2]==0x0C && buffer[3]==0x01)
784 ringtone->Format = RING_NOKIABINARY;
786 if (buffer[0]==0x00 && buffer[1]==0x00 &&
787 buffer[2]==0x00)
789 ringtone->Format = RING_NOKIABINARY;
791 if (buffer[0]==0x4D && buffer[1]==0x54 &&
792 buffer[2]==0x68 && buffer[3]==0x64)
794 ringtone->Format = RING_MIDI;
799 rewind(file);
800 switch (ringtone->Format) {
801 case RING_NOTETONE:
802 if (buffer[0]==0x02 && buffer[1]==0x4A) {
803 error=loadott(file,ringtone);
804 } else if (buffer[0]==0xC7 && buffer[1]==0x45) {
805 error=loadcommunicator(file,ringtone);
806 } else {
807 error=loadrttl(file,ringtone);
809 ringtone->NoteTone.AllNotesScale=false;
810 break;
811 case RING_NOKIABINARY:
812 if (buffer[0]==0x00 && buffer[1]==0x00 &&
813 buffer[2]==0x0C && buffer[3]==0x01)
815 error=loadbin(file,ringtone);
817 if (buffer[0]==0x00 && buffer[1]==0x00 &&
818 buffer[2]==0x00)
820 error=loadre(file,ringtone);
822 break;
823 case RING_MIDI:
824 EncodeUnicode(ringtone->Name,FileName,strlen(FileName));
825 error = loadpuremidi(file,ringtone);
827 fclose(file);
828 return(error);
831 /* -------------------------- required with Nokia & RTTL ------------------- */
833 /* Beats-per-Minute Encoding */
834 static int BeatsPerMinute[] = {
835 25, 28, 31, 35, 40, 45, 50, 56, 63, 70,
836 80, 90, 100, 112, 125, 140, 160, 180, 200, 225,
837 250, 285, 320, 355, 400, 450, 500, 565, 635, 715,
838 800, 900
841 int GSM_RTTLGetTempo(int Beats)
843 size_t i=0;
845 while ( i < sizeof(BeatsPerMinute)/sizeof(BeatsPerMinute[0])) {
846 if (Beats<=BeatsPerMinute[i]) break;
847 i++;
849 return i<<3;
852 /* This function packs the ringtone from the structure "ringtone" to
853 "package", where maxlength means length of package.
854 Function returns number of packed notes and change maxlength to
855 number of used chars in "package" */
856 unsigned char GSM_EncodeNokiaRTTLRingtone(GSM_Ringtone ringtone, unsigned char *package, int *maxlength)
858 unsigned char CommandLength = 0x02;
859 unsigned char Loop = 0x15; /* Infinite */
861 unsigned char Buffer[200];
862 int StartBit=0, OldStartBit;
863 int StartBitHowManyCommands;
864 int HowManyCommands = 0; /* How many instructions packed */
865 int HowManyNotes = 0;
866 int i;
867 bool started;
868 GSM_RingNote *Note;
870 GSM_RingNoteScale DefScale = 255;
871 GSM_RingNoteStyle DefStyle = 255;
872 int DefTempo = 255;
874 StartBit=BitPackByte(package, StartBit, CommandLength, 8);
875 StartBit=BitPackByte(package, StartBit, RingingToneProgramming, 7);
877 /* <command-part> is always octet-aligned. */
878 StartBit=OctetAlign(package, StartBit);
880 StartBit=BitPackByte(package, StartBit, Sound, 7);
881 StartBit=BitPackByte(package, StartBit, BasicSongType, 3);
883 /* Packing the name of the tune. */
884 EncodeUnicodeSpecialNOKIAChars(Buffer, ringtone.Name, UnicodeLength(ringtone.Name));
885 StartBit=BitPackByte(package, StartBit, ((unsigned char)(UnicodeLength(Buffer)<<4)), 4);
886 StartBit=BitPack(package, StartBit, DecodeUnicodeString(Buffer), 8*UnicodeLength(Buffer));
888 /* Info about song pattern */
889 StartBit=BitPackByte(package, StartBit, 0x01, 8); /* One song pattern */
890 StartBit=BitPackByte(package, StartBit, PatternHeaderId, 3);
891 StartBit=BitPackByte(package, StartBit, A_part, 2);
892 StartBit=BitPackByte(package, StartBit, ((unsigned char)(Loop<<4)), 4);
894 /* Later here will be HowManyCommands */
895 StartBitHowManyCommands=StartBit;
896 StartBit = StartBit + 8;
898 started = false;
899 for (i=0; i<ringtone.NoteTone.NrCommands; i++) {
900 if (ringtone.NoteTone.Commands[i].Type == RING_Note) {
901 Note = &ringtone.NoteTone.Commands[i].Note;
902 if (!started) {
903 /* First note can't be Pause - it makes problems
904 * for example with PC Composer
906 if (Note->Note != Note_Pause) started = true;
908 if (started) {
909 OldStartBit = StartBit;
910 /* we don't write Scale & Style info before "Pause" note - it saves place */
911 if (Note->Note!=Note_Pause) {
912 if (DefScale != Note->Scale || ringtone.NoteTone.AllNotesScale) {
913 if ((OctetAlignNumber(StartBit+5+8)/8)>(*maxlength)) {
914 StartBit = OldStartBit;
915 break;
917 DefScale = Note->Scale;
918 StartBit=BitPackByte(package, StartBit, ScaleInstructionId, 3);
919 StartBit=BitPackByte(package, StartBit, ((unsigned char)((DefScale-4)<<6)), 2);
920 HowManyCommands++;
922 if (DefStyle != Note->Style) {
923 if ((OctetAlignNumber(StartBit+5+8)/8)>(*maxlength)) {
924 StartBit = OldStartBit;
925 break;
927 DefStyle = Note->Style;
928 StartBit=BitPackByte(package, StartBit, StyleInstructionId, 3);
929 StartBit=BitPackByte(package, StartBit, ((unsigned char)DefStyle), 2);
930 HowManyCommands++;
933 /* Beats per minute/tempo of the tune */
934 if (DefTempo != GSM_RTTLGetTempo(Note->Tempo)) {
935 if ((OctetAlignNumber(StartBit+8+8)/8)>(*maxlength)) {
936 StartBit = OldStartBit;
937 break;
939 DefTempo=GSM_RTTLGetTempo(Note->Tempo);
940 /* Beats per minute/tempo of the tune */
941 StartBit=BitPackByte(package, StartBit, TempoInstructionId, 3);
942 StartBit=BitPackByte(package, StartBit, ((unsigned char)DefTempo), 5);
943 HowManyCommands++;
945 if ((OctetAlignNumber(StartBit+12+8)/8)>(*maxlength)) {
946 StartBit = OldStartBit;
947 break;
949 /* Note */
950 StartBit=BitPackByte(package, StartBit, NoteInstructionId, 3);
951 StartBit=BitPackByte(package, StartBit, ((unsigned char)Note->Note), 4);
952 StartBit=BitPackByte(package, StartBit, ((unsigned char)Note->Duration), 3);
953 StartBit=BitPackByte(package, StartBit, ((unsigned char)Note->DurationSpec), 2);
954 HowManyCommands++;
955 /* We are sure, we pack it for SMS or setting to phone, not for OTT file */
956 if (*maxlength<1000) {
957 /* Like Pc Composer say - before of phone limitations...*/
958 if (HowManyNotes==130-1) break;
962 HowManyNotes++;
965 StartBit=OctetAlign(package, StartBit);
966 StartBit=BitPackByte(package, StartBit, CommandEnd, 8);
968 OldStartBit = StartBit;
969 StartBit = StartBitHowManyCommands;
970 /* HowManyCommands */
971 StartBit = BitPackByte(package, StartBit, ((unsigned char)HowManyCommands), 8);
972 StartBit = OldStartBit;
974 *maxlength=StartBit/8;
976 return(i);
979 GSM_Error GSM_DecodeNokiaRTTLRingtone(GSM_Ringtone *ringtone, unsigned char *package, int maxlength)
981 int StartBit=0, HowMany, l, q, i, spec;
982 char Buffer[100];
983 GSM_RingNote *Note;
985 /* Default ringtone parameters */
986 GSM_RingNoteScale DefScale = Scale_880;
987 GSM_RingNoteStyle DefStyle = NaturalStyle;
988 int DefTempo = 63;
990 ringtone->Format = RING_NOTETONE;
991 ringtone->NoteTone.NrCommands = 0;
993 StartBit=BitUnPackInt(package,StartBit,&l,8);
994 if (l!=0x02) {
995 dbgprintf("Not header\n");
996 return GE_NOTSUPPORTED;
999 StartBit=BitUnPackInt(package,StartBit,&l,7);
1000 if (l!=RingingToneProgramming) {
1001 dbgprintf("Not RingingToneProgramming\n");
1002 return GE_NOTSUPPORTED;
1005 /* <command-part> is always octet-aligned. */
1006 StartBit=OctetUnAlign(StartBit);
1008 StartBit=BitUnPackInt(package,StartBit,&l,7);
1009 if (l!=Sound) {
1010 dbgprintf("Not Sound\n");
1011 return GE_NOTSUPPORTED;
1014 StartBit=BitUnPackInt(package,StartBit,&l,3);
1015 if (l!=BasicSongType) {
1016 dbgprintf("Not BasicSongType\n");
1017 return GE_NOTSUPPORTED;
1020 /* Getting length of the tune name */
1021 StartBit=BitUnPackInt(package,StartBit,&l,4);
1022 l=l>>4;
1024 /* Unpacking the name of the tune. */
1025 StartBit=BitUnPack(package, StartBit, Buffer, 8*l);
1026 Buffer[l]=0;
1027 EncodeUnicode(ringtone->Name,Buffer,strlen(Buffer));
1028 DecodeUnicodeSpecialNOKIAChars(Buffer, ringtone->Name, UnicodeLength(ringtone->Name));
1029 CopyUnicodeString(ringtone->Name,Buffer);
1031 StartBit=BitUnPackInt(package,StartBit,&l,8);
1032 dbgprintf("Number of song patterns: %i\n",l);
1033 /* we support only one song pattern */
1034 if (l!=1) return GE_NOTSUPPORTED;
1036 StartBit=BitUnPackInt(package,StartBit,&l,3);
1037 if (l!=PatternHeaderId) {
1038 dbgprintf("Not PatternHeaderId\n");
1039 return GE_NOTSUPPORTED;
1042 /* Pattern ID - we ignore it */
1043 StartBit+=2;
1045 StartBit=BitUnPackInt(package,StartBit,&l,4);
1046 l=l>>4;
1047 dbgprintf("Loop value: %i\n",l);
1049 HowMany=0;
1050 StartBit=BitUnPackInt(package, StartBit, &HowMany, 8);
1052 for (i=0;i<HowMany;i++) {
1053 StartBit=BitUnPackInt(package,StartBit,&q,3);
1054 switch (q) {
1055 case VolumeInstructionId:
1056 StartBit+=4;
1057 break;
1058 case StyleInstructionId:
1059 StartBit=BitUnPackInt(package,StartBit,&l,2);
1060 if (l>=NaturalStyle && l<=StaccatoStyle) DefStyle = l;
1061 break;
1062 case TempoInstructionId:
1063 StartBit=BitUnPackInt(package,StartBit,&l,5);
1064 DefTempo=BeatsPerMinute[l>>3];
1065 break;
1066 case ScaleInstructionId:
1067 StartBit=BitUnPackInt(package,StartBit,&l,2);
1068 DefScale=(l>>6)+4;
1069 break;
1070 case NoteInstructionId:
1071 Note = &ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Note;
1072 ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_Note;
1074 StartBit=BitUnPackInt(package,StartBit,&l,4);
1075 Note->Note=Note_Pause;
1076 if (l >= Note_C && l <= Note_H) Note->Note = l;
1078 StartBit=BitUnPackInt(package,StartBit,&l,3);
1079 if (l >= Duration_Full && l <= Duration_1_32) Note->Duration = l;
1081 StartBit=BitUnPackInt(package,StartBit,&spec,2);
1082 if (spec >= NoSpecialDuration && spec <= Length_2_3) {
1083 Note->DurationSpec = spec;
1086 Note->Scale = DefScale;
1087 Note->Style = DefStyle;
1088 Note->Tempo = DefTempo;
1089 if (ringtone->NoteTone.NrCommands==MAX_RINGTONE_NOTES) break;
1090 ringtone->NoteTone.NrCommands++;
1091 break;
1092 default:
1093 dbgprintf("Unsupported block %i %i\n",q,i);
1094 return GE_NOTSUPPORTED;
1097 return GE_NONE;
1100 static void RTTL2Binary(GSM_Ringtone *dest, GSM_Ringtone *src)
1102 int current = 0, i, note, lastnote = 0, duration;
1103 GSM_RingNote *Note;
1105 unsigned char tail[] =
1106 {0x40, 0x7D, 0x40, 0x5C, 0x0A, 0xFE, 0x40,
1107 0x20, 0x40, 0x7D, 0x40, 0x37, 0x0A, 0xFE,
1108 0x0A, 0x0A, 0x40, 0x32, 0x07, 0x0B};
1110 strcpy(dest->NokiaBinary.Frame+current,"\x02\xFC\x09"); current=current+3;
1111 dest->NokiaBinary.Frame[current++]=0x00;
1113 /* This command can be used to loop, where 0xLL = 0x01 - 0x10
1114 * 0x01=loop once [...] 0x10=loop infinite
1115 * Commented now
1117 dest->NokiaBinary.Frame[current++]=0x05;
1118 dest->NokiaBinary.Frame[current++]=0xLL;
1120 strcpy(dest->NokiaBinary.Frame+current,"\x0A\x01"); current=current+2;
1122 for (i=0; i<src->NoteTone.NrCommands; i++) {
1123 switch (src->NoteTone.Commands[i].Type) {
1124 case RING_Note:
1125 Note = &src->NoteTone.Commands[i].Note;
1126 note = 64; /* Pause */
1127 if (Note->Note!=Note_Pause) {
1128 if (Note->Note >= Note_C && Note->Note <= Note_H) {
1129 note = 113 + Note->Note/16;
1131 switch (Note->Scale) {
1132 case Scale_440 : break;
1133 case Scale_880 : note = note + 12; break;
1134 case Scale_1760: note = note + 24; break;
1135 case Scale_3520: note = note + 36; break;
1136 default : break;
1140 /* in 8 ms ticks of 7110 */
1141 duration = 60000 * GSM_RingNoteGetFullDuration(*Note) / Note->Tempo / 256;
1143 switch (Note->Style) {
1144 case StaccatoStyle:
1145 if (duration) {
1146 /* note only for one tick */
1147 dest->NokiaBinary.Frame[current++] = note;
1148 dest->NokiaBinary.Frame[current++] = 1;
1149 duration--;
1151 /* rest pause */
1152 note = 0x40;
1153 case NaturalStyle:
1154 if (note != 0x40 && duration) {
1155 dest->NokiaBinary.Frame[current++] = 0x40;
1156 /* small pause between notes */
1157 dest->NokiaBinary.Frame[current++] = 1;
1158 duration--;
1160 default:
1161 if (note != 0x40 && note == lastnote && duration) {
1162 dest->NokiaBinary.Frame[current++] = 0x40;
1163 /* small pause between same notes */
1164 dest->NokiaBinary.Frame[current++] = 1;
1165 duration--;
1167 while (duration > 125) {
1168 dest->NokiaBinary.Frame[current++] = note;
1169 dest->NokiaBinary.Frame[current++] = 125;
1170 duration -= 125;
1172 dest->NokiaBinary.Frame[current++] = note;
1173 dest->NokiaBinary.Frame[current++] = duration;
1175 lastnote = note;
1176 default:
1177 break;
1180 for (i = 0; i < (int)sizeof(tail); i++) dest->NokiaBinary.Frame[current++] = tail[i];
1181 dest->NokiaBinary.Length=current;
1184 static void Binary2RTTL(GSM_Ringtone *dest, GSM_Ringtone *src)
1186 int i = 3, j, z, NrNotes = 0, repeat = 0, accuracy;
1187 int StartRepeat = 0, EndRepeat, Speed;
1188 unsigned char command,length=0;
1189 int NotesLen[500];
1190 GSM_RingNoteScale NotesScale[500];
1191 GSM_RingNoteNote Notes[500];
1192 int Lengths[6*4];
1193 GSM_RingNoteDurationSpec DurationSpec[6*4];
1194 GSM_RingNoteDuration Duration[6*4];
1195 bool foundlen;
1196 GSM_RingNote *Note;
1198 while (i<src->NokiaBinary.Length) {
1199 command = src->NokiaBinary.Frame[i];
1200 i++;
1201 if (command != 0x06 && command != 0x00 && command != 0x09) {
1202 length = src->NokiaBinary.Frame[i];
1203 i++;
1204 dbgprintf("Block %02x %02x - ",length,command);
1205 } else dbgprintf("Block %02x - ",command);
1206 if (command >= 114 && command <= 161) {
1207 dbgprintf("note\n");
1208 if (command >= 114 && command <= 124) {
1209 NotesScale[NrNotes] = Scale_440; command -= 114;
1210 } else if (command >= 125 && command <= 137) {
1211 NotesScale[NrNotes] = Scale_880; command -= 126;
1212 } else if (command >= 138 && command <= 149) {
1213 NotesScale[NrNotes] = Scale_1760; command -= 138;
1214 } else if (command >= 150 && command <= 161) {
1215 NotesScale[NrNotes] = Scale_3520; command -= 150;
1217 switch (command) {
1218 case 0 : Notes[NrNotes] = Note_C; break;
1219 case 1 : Notes[NrNotes] = Note_Cis; break;
1220 case 2 : Notes[NrNotes] = Note_D; break;
1221 case 3 : Notes[NrNotes] = Note_Dis; break;
1222 case 4 : Notes[NrNotes] = Note_E; break;
1223 case 5 : Notes[NrNotes] = Note_F; break;
1224 case 6 : Notes[NrNotes] = Note_Fis; break;
1225 case 7 : Notes[NrNotes] = Note_G; break;
1226 case 8 : Notes[NrNotes] = Note_Gis; break;
1227 case 9 : Notes[NrNotes] = Note_A; break;
1228 case 10 : Notes[NrNotes] = Note_Ais; break;
1229 case 11 : Notes[NrNotes] = Note_H; break;
1231 if (NrNotes > 0) {
1232 if (Notes[NrNotes-1] == Notes[NrNotes] &&
1233 NotesScale[NrNotes-1] == NotesScale[NrNotes]) {
1234 NotesLen[NrNotes-1]+=length;
1235 } else {
1236 NotesLen[NrNotes]=length;
1237 NrNotes++;
1239 } else {
1240 NotesLen[NrNotes]=length;
1241 NrNotes++;
1243 } else switch (command) {
1244 case 0x00:
1245 dbgprintf("Unknown\n");
1246 break;
1247 case 0x05:
1248 dbgprintf("repeat %i times\n",length);
1249 repeat = length;
1250 StartRepeat = NrNotes;
1251 break;
1252 case 0x06:
1253 dbgprintf("end repeat\n");
1254 EndRepeat = NrNotes;
1255 for (z=0;z<repeat-1;z++) {
1256 for (j=StartRepeat;j<EndRepeat;j++) {
1257 Notes[NrNotes] = Notes[j];
1258 NotesScale[NrNotes] = NotesScale[j];
1259 NotesLen[NrNotes] = NotesLen[j];
1260 NrNotes++;
1261 dbgprintf("Adding repeat note %i %i\n",Notes[j],NotesLen[j]);
1264 break;
1265 case 0x07:
1266 if (length == 0x0B) {
1267 dbgprintf("Ringtone end\n");
1268 i = src->NokiaBinary.Length + 1;
1270 break;
1271 case 0x09:
1272 dbgprintf("Unknown\n");
1273 break;
1274 case 0x0A:
1275 if (length == 0x01) {
1276 dbgprintf("Let's start our song\n");
1277 break;
1279 if (length == 0x0A) {
1280 dbgprintf("Ending joining note\n");
1281 break;
1283 if (length == 0xFE) {
1284 dbgprintf("Starting joining note\n");
1285 break;
1287 break;
1288 case 0x40:
1289 dbgprintf("Pause\n");
1290 Notes[NrNotes] = Note_Pause;
1291 if (NrNotes > 0) {
1292 if (Notes[NrNotes-1] == Notes[NrNotes] &&
1293 NotesScale[NrNotes-1] == NotesScale[NrNotes]) {
1294 NotesLen[NrNotes-1]+=length;
1295 } else {
1296 NotesLen[NrNotes]=length;
1297 NrNotes++;
1299 } else {
1300 NotesLen[NrNotes]=length;
1301 NrNotes++;
1303 break;
1304 default:
1305 dbgprintf("Unknown\n");
1309 while (NrNotes>0) {
1310 if (Notes[NrNotes-1] == Note_Pause) {
1311 NrNotes--;
1312 } else break;
1315 for (accuracy=1; accuracy<5; accuracy++) {
1316 i = 1;
1317 while (i < 1000) {
1318 Lengths[0] = 30000/i;
1319 for (j=0;j<5;j++) Lengths[j+1] = Lengths[j] / 2;
1320 for (j=0;j<6;j++) Lengths[6+j] = Lengths[j] * 3/2;
1321 for (j=0;j<6;j++) Lengths[12+j] = Lengths[j] * 9/4;
1322 for (j=0;j<6;j++) Lengths[18+j] = Lengths[j] * 2/3;
1324 #ifdef DEBUG
1325 dbgprintf("Length matrix (%i) : ",i);
1326 for (j=0;j<6*4;j++) dbgprintf("%i ",Lengths[j]);
1327 dbgprintf("\n");
1328 #endif
1329 foundlen = false;
1331 for (j=0;j<NrNotes;j++) {
1332 dbgprintf("Comparing to %i\n",NotesLen[j]);
1333 foundlen = false;
1334 for (z=0;z<6*4;z++) {
1335 if (NotesLen[j] - Lengths[z] > -accuracy &&
1336 NotesLen[j] - Lengths[z] < accuracy) {
1337 foundlen = true;
1338 break;
1341 if (!foundlen) break;
1343 if (foundlen) break;
1344 i++;
1347 if (foundlen) {
1348 Speed = i;
1349 Duration[5] = Duration_1_32; Duration[4] = Duration_1_16;
1350 Duration[3] = Duration_1_8; Duration[2] = Duration_1_4;
1351 Duration[1] = Duration_1_2; Duration[0] = Duration_Full;
1352 for (i=0;i<6;i++) Duration[i] = Duration[i];
1353 for (i=0;i<6;i++) Duration[i+6] = Duration[i];
1354 for (i=0;i<6;i++) Duration[i+12] = Duration[i];
1355 for (i=0;i<6;i++) Duration[i+18] = Duration[i];
1356 for (i=0;i<6;i++) DurationSpec[i] = NoSpecialDuration;
1357 for (i=0;i<6;i++) DurationSpec[i+6] = DottedNote;
1358 for (i=0;i<6;i++) DurationSpec[i+12] = DoubleDottedNote;
1359 for (i=0;i<6;i++) DurationSpec[i+18] = Length_2_3;
1361 for (i=0;i<NrNotes;i++) {
1362 dest->NoteTone.Commands[i].Type = RING_Note;
1363 Note = &dest->NoteTone.Commands[i].Note;
1364 Note->Note = Notes[i];
1365 Note->Tempo = Speed;
1366 Note->Style = ContinuousStyle;
1367 if (Notes[i] != Note_Pause) Note->Scale = NotesScale[i];
1368 for (z=0;z<6*4;z++) {
1369 if (NotesLen[i] - Lengths[z] > -accuracy &&
1370 NotesLen[i] - Lengths[z] < accuracy) {
1371 Note->Duration = Duration[z];
1372 Note->DurationSpec = DurationSpec[z];
1373 /* Trick from PPM Edit */
1374 if (Note->DurationSpec == DoubleDottedNote) {
1375 switch (Note->Duration) {
1376 case Duration_Full:Note->Duration = Duration_Full;break;
1377 case Duration_1_2 :Note->Duration = Duration_Full;break;
1378 case Duration_1_4 :Note->Duration = Duration_1_2; break;
1379 case Duration_1_8 :Note->Duration = Duration_1_4; break;
1380 case Duration_1_16:Note->Duration = Duration_1_8; break;
1381 case Duration_1_32:Note->Duration = Duration_1_16;break;
1383 Note->DurationSpec = NoSpecialDuration;
1385 /* Here happy creation */
1386 if (Note->DurationSpec == Length_2_3) {
1387 Note->DurationSpec = NoSpecialDuration;
1390 break;
1394 dest->NoteTone.NrCommands = NrNotes;
1395 dbgprintf("speed = %i\n",Speed);
1396 break;
1400 if (!foundlen) dest->NoteTone.NrCommands = 0;
1403 GSM_Error GSM_RingtoneConvert(GSM_Ringtone *dest, GSM_Ringtone *src, GSM_RingtoneFormat Format)
1405 dest->Format = Format;
1406 CopyUnicodeString(dest->Name,src->Name);
1407 if (src->Format==RING_NOTETONE && Format==RING_NOKIABINARY) {
1408 RTTL2Binary(dest, src);
1409 return GE_NONE;
1411 if (src->Format==RING_NOKIABINARY && Format==RING_NOTETONE) {
1412 Binary2RTTL(dest, src);
1413 return GE_NONE;
1415 /* The same source and target format */
1416 if (src->Format==Format) {
1417 memcpy(dest,src,sizeof(GSM_Ringtone));
1418 return GE_NONE;
1420 return GE_NOTIMPLEMENTED;
1423 /* 0 = No header and footer, 0.5 = partial header and footer,
1424 * 1.0 = IMelody 1.0, 1.2 = IMelody 1.2 */
1425 unsigned char GSM_EncodeEMSSound(GSM_Ringtone ringtone, unsigned char *package, int *maxlength, double version, bool start)
1427 int i, NrNotes = 0, Len, Max = *maxlength;
1429 GSM_RingNote *Note;
1431 GSM_RingNoteScale DefNoteScale;
1432 GSM_RingNoteStyle DefNoteStyle=0;
1433 int DefNoteTempo=0;
1435 bool started = false, end;
1437 *maxlength = 0;
1439 if (start) {
1440 if (version != 0) *maxlength+=sprintf(package,"BEGIN:IMELODY%c%c",13,10);
1441 if (version == 1.0) *maxlength+=sprintf(package+(*maxlength),"VERSION:1.0%c%c",13,10);
1442 if (version == 1.2) *maxlength+=sprintf(package+(*maxlength),"VERSION:1.2%c%c",13,10);
1443 if (version >= 1.0) *maxlength+=sprintf(package+(*maxlength),"FORMAT:CLASS1.0%c%c",13,10);
1444 if (version == 1.2) *maxlength+=sprintf(package+(*maxlength),"NAME:%s%c%c",DecodeUnicodeString(ringtone.Name),13,10);
1447 DefNoteScale = Scale_880; /* by iMelody definition */
1449 for (i=0;i<ringtone.NoteTone.NrCommands;i++) {
1450 Len = *maxlength;
1451 if (ringtone.NoteTone.Commands[i].Type == RING_Note) {
1452 Note = &ringtone.NoteTone.Commands[i].Note;
1453 if (Note->Note != Note_Pause) {
1454 if (version == 1.2 && start) {
1455 /* Save the default tempo */
1456 DefNoteTempo = Note->Tempo;
1457 Len+=sprintf(package+Len,"BEAT:%i%c%c",DefNoteTempo,13,10);
1458 dbgprintf("DefNoteTempo=%d\n",DefNoteTempo);
1460 /* Save default style */
1461 DefNoteStyle = Note->Style;
1462 switch (DefNoteStyle) {
1463 case NaturalStyle :Len+=sprintf(package+Len,"STYLE:S0%c%c",13,10); break;
1464 case ContinuousStyle:Len+=sprintf(package+Len,"STYLE:S1%c%c",13,10); break;
1465 case StaccatoStyle :Len+=sprintf(package+Len,"STYLE:S2%c%c",13,10); break;
1468 Len+=sprintf(package+Len,"MELODY:");
1469 if (version != 0) {
1470 /* 15 = Len of END:IMELODY... */
1471 if ((Len+15) > Max) { end = true; break; }
1472 } else {
1473 if (Len > Max) { end = true; break; }
1475 *maxlength = Len;
1476 break;
1481 for (i=0;i<ringtone.NoteTone.NrCommands;i++) {
1482 end = false;
1483 Len = *maxlength;
1484 switch (ringtone.NoteTone.Commands[i].Type) {
1485 case RING_Note:
1486 Note = &ringtone.NoteTone.Commands[i].Note;
1487 if (!started && Note->Note != Note_Pause) started = true;
1488 if (started) {
1489 if (Note->Note!=Note_Pause && Note->Scale != DefNoteScale)
1491 Len+=sprintf(package+Len,"*%i",Note->Scale-1);
1493 switch (Note->Note) {
1494 case Note_C :Len+=sprintf(package+Len,"c"); break;
1495 case Note_Cis :Len+=sprintf(package+Len,"#c");break;
1496 case Note_D :Len+=sprintf(package+Len,"d"); break;
1497 case Note_Dis :Len+=sprintf(package+Len,"#d");break;
1498 case Note_E :Len+=sprintf(package+Len,"e"); break;
1499 case Note_F :Len+=sprintf(package+Len,"f"); break;
1500 case Note_Fis :Len+=sprintf(package+Len,"#f");break;
1501 case Note_G :Len+=sprintf(package+Len,"g"); break;
1502 case Note_Gis :Len+=sprintf(package+Len,"#g");break;
1503 case Note_A :Len+=sprintf(package+Len,"a"); break;
1504 case Note_Ais :Len+=sprintf(package+Len,"#a");break;
1505 case Note_H :Len+=sprintf(package+Len,"b"); break;
1506 case Note_Pause :Len+=sprintf(package+Len,"r"); break;
1508 switch (Note->Duration) {
1509 case Duration_Full : package[Len++]='0'; break;
1510 case Duration_1_2 : package[Len++]='1'; break;
1511 case Duration_1_4 : package[Len++]='2'; break;
1512 case Duration_1_8 : package[Len++]='3'; break;
1513 case Duration_1_16 : package[Len++]='4'; break;
1514 case Duration_1_32 : package[Len++]='5'; break;
1515 default : break;
1517 switch (Note->DurationSpec) {
1518 case DottedNote : package[Len++] = '.'; break;
1519 case DoubleDottedNote : package[Len++] = ':'; break;
1520 case Length_2_3 : package[Len++] = ';'; break;
1521 default : break;
1523 if (version != 0) {
1524 /* 15 = Len of END:IMELODY... */
1525 if ((Len+15) > Max) { end = true; break; }
1526 } else {
1527 if (Len > Max) { end = true; break; }
1529 *maxlength = Len;
1531 break;
1532 case RING_DisableLED:
1533 if ((Len + 6) > Max) { end = true; break; }
1534 (*maxlength)+=sprintf(package+Len,"ledoff");
1535 break;
1536 case RING_EnableLED:
1537 if ((Len + 5) > Max) { end = true; break; }
1538 (*maxlength)+=sprintf(package+Len,"ledon");
1539 break;
1540 case RING_DisableVibra:
1541 if ((Len + 7) > Max) { end = true; break; }
1542 (*maxlength)+=sprintf(package+Len,"vibeoff");
1543 break;
1544 case RING_EnableVibra:
1545 if ((Len + 6) > Max) { end = true; break; }
1546 (*maxlength)+=sprintf(package+Len,"vibeon");
1547 break;
1548 case RING_DisableLight:
1549 if ((Len + 7) > Max) { end = true; break; }
1550 (*maxlength)+=sprintf(package+Len,"backoff");
1551 break;
1552 case RING_EnableLight:
1553 if ((Len + 6) > Max) { end = true; break; }
1554 (*maxlength)+=sprintf(package+Len,"backon");
1555 break;
1556 default:
1557 break;
1559 if (end) break;
1560 NrNotes ++;
1563 if (version != 0) *maxlength+=sprintf(package+(*maxlength),"%c%cEND:IMELODY%c%c",13,10,13,10);
1565 return NrNotes;
1568 char *GSM_GetRingtoneName(GSM_AllRingtonesInfo *Info, int ID)
1570 int i;
1571 static char ala[2];
1573 for (i=0;i<Info->Number;i++) {
1574 if (Info->Ringtone[i].ID == ID) return Info->Ringtone[i].Name;
1577 ala[0] = 0;
1578 ala[1] = 0;
1579 return ala;
1582 /* How should editor hadle tabs in this file? Add editor commands here.
1583 * vim: noexpandtab sw=8 ts=8 sts=8: