10 #include "../gsmcomon.h"
11 #include "../misc/coding/coding.h"
12 #include "../gsmstate.h"
14 #include "sms/gsmsms.h"
16 int GSM_RingNoteGetFrequency(GSM_RingNote Note
)
20 /* Values according to the software from http://iki.fi/too/sw/xring/
22 * perl -e 'print int(4400 * (2 **($_/12)) + .5)/10, "\n" for(3..14)'
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;
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;
49 int GSM_RingNoteGetFullDuration(GSM_RingNote Note
)
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;
71 # define PI 3.141592654
74 #define WAV_SAMPLE_RATE 44100
76 static GSM_Error
savewav(FILE *file
, GSM_Ringtone
*ringtone
)
78 unsigned char WAV_Header
[] = {
80 0x00,0x00,0x00,0x00, /* Length */
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
[] = {
87 0x00,0x00,0x00,0x00}; /* Length */
88 short DATA_Buffer
[60000];
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
;
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
);
130 static GSM_Error
savebin(FILE *file
, GSM_Ringtone
*ringtone
)
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
);
144 static GSM_Error
savepuremidi(FILE *file
, GSM_Ringtone
*ringtone
)
146 fwrite(ringtone
->NokiaBinary
.Frame
,1,ringtone
->NokiaBinary
.Length
,file
);
150 GSM_Error
saverttl(FILE *file
, GSM_Ringtone
*ringtone
)
152 GSM_RingNoteScale DefNoteScale
;
153 GSM_RingNoteDuration DefNoteDuration
;
155 GSM_RingNoteStyle DefNoteStyle
=0;
158 bool started
= false, firstcomma
= true;
161 unsigned char buffer
[15];
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 */
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]++;
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
;
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
);
256 if (Note
->Style
!=DefNoteStyle
) {
257 /* And a separator */
258 if (!firstcomma
) fprintf(file
,",");
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
,",");
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
,",");
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;
288 /* And a separator */
289 if (!firstcomma
) fprintf(file
,",");
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;
312 if (Note
->Note
!=Note_Pause
&& Note
->Scale
!= DefNoteScale
) {
313 fprintf(file
,"%i",Note
->Scale
);
321 static void saveimelody(FILE *file
, GSM_Ringtone
*ringtone
)
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
)
335 buffer
= value
& 0x7f;
337 while (value
>>= 7) {
340 buffer
+= (value
& 0x7f);
344 midifile
[(*current
)++] = (unsigned char)buffer
;
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;
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
;
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
;
388 duration
= GSM_RingNoteGetFullDuration(*Note
);
389 if (Note
->Note
== Note_Pause
) {
392 WriteVarLen(midifile
,¤t
,pause
);
394 midifile
[current
++]=0x00; // pause
395 midifile
[current
++]=0x00;
398 if (Note
->Note
>= Note_C
&& Note
->Note
<= Note_H
) {
399 note
= Note
->Note
/16 + 12 * Note
->Scale
- 1;
402 WriteVarLen(midifile
,¤t
,pause
);
404 midifile
[current
++]=0x90; // note on
405 midifile
[current
++]=note
;
406 midifile
[current
++]=0x64; // forte
408 WriteVarLen(midifile
,¤t
,duration
);
409 midifile
[current
++]=0x80; // note off
410 midifile
[current
++]=note
;
411 midifile
[current
++]=0x64;
417 WriteVarLen(midifile
,¤t
,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
)
436 GSM_EncodeNokiaRTTLRingtone(*ringtone
, Buffer
, &i
);
438 fwrite(Buffer
, 1, i
, file
);
441 GSM_Error
GSM_SaveRingtoneFile(char *FileName
, GSM_Ringtone
*ringtone
)
445 file
= fopen(FileName
, "wb");
446 if (file
== NULL
) return(GE_CANTOPENFILE
);
448 switch (ringtone
->Format
) {
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
);
463 saverttl(file
, ringtone
);
466 case RING_NOKIABINARY
:
467 savebin(file
, ringtone
);
470 savepuremidi(file
, ringtone
);
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
;
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
);
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. */
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;
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;
532 DefNoteTempo
=atoi(ptr
+2);
533 dbgprintf("Tempo = %i\n",DefNoteTempo
);
537 case 'C': case 'c': DefNoteStyle
=ContinuousStyle
; break;
538 case 'N': case 'n': DefNoteStyle
=NaturalStyle
; break;
539 case 'S': case 's': DefNoteStyle
=StaccatoStyle
; break;
542 case 'C': case 'c': DefNoteStyle
=ContinuousStyle
; break;
543 case 'N': case 'n': DefNoteStyle
=NaturalStyle
; break;
544 case 'S': case 's': DefNoteStyle
=StaccatoStyle
; 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
) {
559 ringtone
->NoteTone
.Commands
[ringtone
->NoteTone
.NrCommands
].Type
= RING_DisableLED
;
560 ringtone
->NoteTone
.NrCommands
++;
563 ringtone
->NoteTone
.Commands
[ringtone
->NoteTone
.NrCommands
].Type
= RING_EnableLED
;
564 ringtone
->NoteTone
.NrCommands
++;
567 ringtone
->NoteTone
.Commands
[ringtone
->NoteTone
.NrCommands
].Type
= RING_DisableVibra
;
568 ringtone
->NoteTone
.NrCommands
++;
571 ringtone
->NoteTone
.Commands
[ringtone
->NoteTone
.NrCommands
].Type
= RING_EnableVibra
;
572 ringtone
->NoteTone
.NrCommands
++;
575 ringtone
->NoteTone
.Commands
[ringtone
->NoteTone
.NrCommands
].Type
= RING_DisableLight
;
576 ringtone
->NoteTone
.NrCommands
++;
579 ringtone
->NoteTone
.Commands
[ringtone
->NoteTone
.NrCommands
].Type
= RING_EnableLight
;
580 ringtone
->NoteTone
.NrCommands
++;
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;
592 case 'C': case 'c': DefNoteStyle
=ContinuousStyle
; break;
593 case 'N': case 'n': DefNoteStyle
=NaturalStyle
; break;
594 case 'S': case 's': DefNoteStyle
=StaccatoStyle
; break;
597 case 'C': case 'c': DefNoteStyle
=ContinuousStyle
; break;
598 case 'N': case 'n': DefNoteStyle
=NaturalStyle
; break;
599 case 'S': case 's': DefNoteStyle
=StaccatoStyle
; break;
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
;
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>] */
627 Note
->DurationSpec
= DottedNote
;
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 */
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;
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;
658 /* Check for dodgy rttl */
659 /* [<special-duration>] */
661 Note
->DurationSpec
= DottedNote
;
666 if (Note
->Note
!=Note_Pause
) {
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;
678 ringtone
->NoteTone
.NrCommands
++;
681 ptr
=strtok(NULL
, ", ");
686 static GSM_Error
loadott(FILE *file
, GSM_Ringtone
*ringtone
)
691 i
=fread(Buffer
, 1, 2000, file
);
693 return GSM_DecodeNokiaRTTLRingtone(ringtone
, Buffer
, i
);
696 static GSM_Error
loadcommunicator(FILE *file
, GSM_Ringtone
*ringtone
)
701 i
=fread(Buffer
, 1, 4000, file
);
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
;
712 return GSM_DecodeNokiaRTTLRingtone(ringtone
, Buffer
+j
, i
-j
);
715 static GSM_Error
loadbin(FILE *file
, GSM_Ringtone
*ringtone
)
718 unsigned char buffer
[2000];
720 dbgprintf("loading binary\n");
721 ringtone
->NokiaBinary
.Length
=fread(buffer
, 1, 500, file
);
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) {
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
));
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
));
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
);
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
));
767 GSM_Error
GSM_ReadRingtoneFile(char *FileName
, GSM_Ringtone
*ringtone
)
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 &&
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
;
800 switch (ringtone
->Format
) {
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
);
807 error
=loadrttl(file
,ringtone
);
809 ringtone
->NoteTone
.AllNotesScale
=false;
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 &&
820 error
=loadre(file
,ringtone
);
824 EncodeUnicode(ringtone
->Name
,FileName
,strlen(FileName
));
825 error
= loadpuremidi(file
,ringtone
);
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,
841 int GSM_RTTLGetTempo(int Beats
)
845 while ( i
< sizeof(BeatsPerMinute
)/sizeof(BeatsPerMinute
[0])) {
846 if (Beats
<=BeatsPerMinute
[i
]) break;
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;
870 GSM_RingNoteScale DefScale
= 255;
871 GSM_RingNoteStyle DefStyle
= 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;
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
;
903 /* First note can't be Pause - it makes problems
904 * for example with PC Composer
906 if (Note
->Note
!= Note_Pause
) started
= true;
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
;
917 DefScale
= Note
->Scale
;
918 StartBit
=BitPackByte(package
, StartBit
, ScaleInstructionId
, 3);
919 StartBit
=BitPackByte(package
, StartBit
, ((unsigned char)((DefScale
-4)<<6)), 2);
922 if (DefStyle
!= Note
->Style
) {
923 if ((OctetAlignNumber(StartBit
+5+8)/8)>(*maxlength
)) {
924 StartBit
= OldStartBit
;
927 DefStyle
= Note
->Style
;
928 StartBit
=BitPackByte(package
, StartBit
, StyleInstructionId
, 3);
929 StartBit
=BitPackByte(package
, StartBit
, ((unsigned char)DefStyle
), 2);
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
;
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);
945 if ((OctetAlignNumber(StartBit
+12+8)/8)>(*maxlength
)) {
946 StartBit
= OldStartBit
;
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);
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;
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;
979 GSM_Error
GSM_DecodeNokiaRTTLRingtone(GSM_Ringtone
*ringtone
, unsigned char *package
, int maxlength
)
981 int StartBit
=0, HowMany
, l
, q
, i
, spec
;
985 /* Default ringtone parameters */
986 GSM_RingNoteScale DefScale
= Scale_880
;
987 GSM_RingNoteStyle DefStyle
= NaturalStyle
;
990 ringtone
->Format
= RING_NOTETONE
;
991 ringtone
->NoteTone
.NrCommands
= 0;
993 StartBit
=BitUnPackInt(package
,StartBit
,&l
,8);
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);
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);
1024 /* Unpacking the name of the tune. */
1025 StartBit
=BitUnPack(package
, StartBit
, Buffer
, 8*l
);
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 */
1045 StartBit
=BitUnPackInt(package
,StartBit
,&l
,4);
1047 dbgprintf("Loop value: %i\n",l
);
1050 StartBit
=BitUnPackInt(package
, StartBit
, &HowMany
, 8);
1052 for (i
=0;i
<HowMany
;i
++) {
1053 StartBit
=BitUnPackInt(package
,StartBit
,&q
,3);
1055 case VolumeInstructionId
:
1058 case StyleInstructionId
:
1059 StartBit
=BitUnPackInt(package
,StartBit
,&l
,2);
1060 if (l
>=NaturalStyle
&& l
<=StaccatoStyle
) DefStyle
= l
;
1062 case TempoInstructionId
:
1063 StartBit
=BitUnPackInt(package
,StartBit
,&l
,5);
1064 DefTempo
=BeatsPerMinute
[l
>>3];
1066 case ScaleInstructionId
:
1067 StartBit
=BitUnPackInt(package
,StartBit
,&l
,2);
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
++;
1093 dbgprintf("Unsupported block %i %i\n",q
,i
);
1094 return GE_NOTSUPPORTED
;
1100 static void RTTL2Binary(GSM_Ringtone
*dest
, GSM_Ringtone
*src
)
1102 int current
= 0, i
, note
, lastnote
= 0, duration
;
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
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
) {
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;
1140 /* in 8 ms ticks of 7110 */
1141 duration
= 60000 * GSM_RingNoteGetFullDuration(*Note
) / Note
->Tempo
/ 256;
1143 switch (Note
->Style
) {
1146 /* note only for one tick */
1147 dest
->NokiaBinary
.Frame
[current
++] = note
;
1148 dest
->NokiaBinary
.Frame
[current
++] = 1;
1154 if (note
!= 0x40 && duration
) {
1155 dest
->NokiaBinary
.Frame
[current
++] = 0x40;
1156 /* small pause between notes */
1157 dest
->NokiaBinary
.Frame
[current
++] = 1;
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;
1167 while (duration
> 125) {
1168 dest
->NokiaBinary
.Frame
[current
++] = note
;
1169 dest
->NokiaBinary
.Frame
[current
++] = 125;
1172 dest
->NokiaBinary
.Frame
[current
++] = note
;
1173 dest
->NokiaBinary
.Frame
[current
++] = duration
;
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;
1190 GSM_RingNoteScale NotesScale
[500];
1191 GSM_RingNoteNote Notes
[500];
1193 GSM_RingNoteDurationSpec DurationSpec
[6*4];
1194 GSM_RingNoteDuration Duration
[6*4];
1198 while (i
<src
->NokiaBinary
.Length
) {
1199 command
= src
->NokiaBinary
.Frame
[i
];
1201 if (command
!= 0x06 && command
!= 0x00 && command
!= 0x09) {
1202 length
= src
->NokiaBinary
.Frame
[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;
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;
1232 if (Notes
[NrNotes
-1] == Notes
[NrNotes
] &&
1233 NotesScale
[NrNotes
-1] == NotesScale
[NrNotes
]) {
1234 NotesLen
[NrNotes
-1]+=length
;
1236 NotesLen
[NrNotes
]=length
;
1240 NotesLen
[NrNotes
]=length
;
1243 } else switch (command
) {
1245 dbgprintf("Unknown\n");
1248 dbgprintf("repeat %i times\n",length
);
1250 StartRepeat
= NrNotes
;
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
];
1261 dbgprintf("Adding repeat note %i %i\n",Notes
[j
],NotesLen
[j
]);
1266 if (length
== 0x0B) {
1267 dbgprintf("Ringtone end\n");
1268 i
= src
->NokiaBinary
.Length
+ 1;
1272 dbgprintf("Unknown\n");
1275 if (length
== 0x01) {
1276 dbgprintf("Let's start our song\n");
1279 if (length
== 0x0A) {
1280 dbgprintf("Ending joining note\n");
1283 if (length
== 0xFE) {
1284 dbgprintf("Starting joining note\n");
1289 dbgprintf("Pause\n");
1290 Notes
[NrNotes
] = Note_Pause
;
1292 if (Notes
[NrNotes
-1] == Notes
[NrNotes
] &&
1293 NotesScale
[NrNotes
-1] == NotesScale
[NrNotes
]) {
1294 NotesLen
[NrNotes
-1]+=length
;
1296 NotesLen
[NrNotes
]=length
;
1300 NotesLen
[NrNotes
]=length
;
1305 dbgprintf("Unknown\n");
1310 if (Notes
[NrNotes
-1] == Note_Pause
) {
1315 for (accuracy
=1; accuracy
<5; accuracy
++) {
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;
1325 dbgprintf("Length matrix (%i) : ",i
);
1326 for (j
=0;j
<6*4;j
++) dbgprintf("%i ",Lengths
[j
]);
1331 for (j
=0;j
<NrNotes
;j
++) {
1332 dbgprintf("Comparing to %i\n",NotesLen
[j
]);
1334 for (z
=0;z
<6*4;z
++) {
1335 if (NotesLen
[j
] - Lengths
[z
] > -accuracy
&&
1336 NotesLen
[j
] - Lengths
[z
] < accuracy
) {
1341 if (!foundlen
) break;
1343 if (foundlen
) break;
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
;
1394 dest
->NoteTone
.NrCommands
= NrNotes
;
1395 dbgprintf("speed = %i\n",Speed
);
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
);
1411 if (src
->Format
==RING_NOKIABINARY
&& Format
==RING_NOTETONE
) {
1412 Binary2RTTL(dest
, src
);
1415 /* The same source and target format */
1416 if (src
->Format
==Format
) {
1417 memcpy(dest
,src
,sizeof(GSM_Ringtone
));
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
;
1431 GSM_RingNoteScale DefNoteScale
;
1432 GSM_RingNoteStyle DefNoteStyle
=0;
1435 bool started
= false, end
;
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
++) {
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:");
1470 /* 15 = Len of END:IMELODY... */
1471 if ((Len
+15) > Max
) { end
= true; break; }
1473 if (Len
> Max
) { end
= true; break; }
1481 for (i
=0;i
<ringtone
.NoteTone
.NrCommands
;i
++) {
1484 switch (ringtone
.NoteTone
.Commands
[i
].Type
) {
1486 Note
= &ringtone
.NoteTone
.Commands
[i
].Note
;
1487 if (!started
&& Note
->Note
!= Note_Pause
) started
= true;
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;
1517 switch (Note
->DurationSpec
) {
1518 case DottedNote
: package
[Len
++] = '.'; break;
1519 case DoubleDottedNote
: package
[Len
++] = ':'; break;
1520 case Length_2_3
: package
[Len
++] = ';'; break;
1524 /* 15 = Len of END:IMELODY... */
1525 if ((Len
+15) > Max
) { end
= true; break; }
1527 if (Len
> Max
) { end
= true; break; }
1532 case RING_DisableLED
:
1533 if ((Len
+ 6) > Max
) { end
= true; break; }
1534 (*maxlength
)+=sprintf(package
+Len
,"ledoff");
1536 case RING_EnableLED
:
1537 if ((Len
+ 5) > Max
) { end
= true; break; }
1538 (*maxlength
)+=sprintf(package
+Len
,"ledon");
1540 case RING_DisableVibra
:
1541 if ((Len
+ 7) > Max
) { end
= true; break; }
1542 (*maxlength
)+=sprintf(package
+Len
,"vibeoff");
1544 case RING_EnableVibra
:
1545 if ((Len
+ 6) > Max
) { end
= true; break; }
1546 (*maxlength
)+=sprintf(package
+Len
,"vibeon");
1548 case RING_DisableLight
:
1549 if ((Len
+ 7) > Max
) { end
= true; break; }
1550 (*maxlength
)+=sprintf(package
+Len
,"backoff");
1552 case RING_EnableLight
:
1553 if ((Len
+ 6) > Max
) { end
= true; break; }
1554 (*maxlength
)+=sprintf(package
+Len
,"backon");
1563 if (version
!= 0) *maxlength
+=sprintf(package
+(*maxlength
),"%c%cEND:IMELODY%c%c",13,10,13,10);
1568 char *GSM_GetRingtoneName(GSM_AllRingtonesInfo
*Info
, int ID
)
1573 for (i
=0;i
<Info
->Number
;i
++) {
1574 if (Info
->Ringtone
[i
].ID
== ID
) return Info
->Ringtone
[i
].Name
;
1582 /* How should editor hadle tabs in this file? Add editor commands here.
1583 * vim: noexpandtab sw=8 ts=8 sts=8: