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