7 A Linux/Unix toolset and driver for Nokia mobile phones.
9 Copyright (C) 1999, 2000 Hugh Blemings & Pavel JanÃk ml.
11 Released under the terms of the GNU GPL, see file COPYING for more details.
13 This file provides support for ringtones.
16 Revision 1.1.1.1 2001/11/25 21:59:00 short
17 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
19 Revision 1.3 2001/11/08 16:34:19 pkot
20 Updates to work with new libsms
22 Revision 1.2 2001/09/20 21:46:21 pkot
23 Locale cleanups (Pawel Kot)
28 #include "gsm-ringtones.h"
31 /* Beats-per-Minute Encoding */
33 int BeatsPerMinute[] = {
68 int OctetAlign(unsigned char *Dest, int CurrentBit)
72 while((CurrentBit+i)%8) {
73 ClearBit(Dest, CurrentBit+i);
80 int OctetAlignNumber(int CurrentBit)
84 while((CurrentBit+i)%8) {
91 int BitPack(unsigned char *Dest, int CurrentBit, unsigned char *Source, int Bits)
95 for (i=0; i<Bits; i++)
96 if (GetBit(Source, i))
97 SetBit(Dest, CurrentBit+i);
99 ClearBit(Dest, CurrentBit+i);
101 return CurrentBit+Bits;
104 int GetTempo(int Beats)
108 while ( i < sizeof(BeatsPerMinute)/sizeof(BeatsPerMinute[0])) {
110 if (Beats<=BeatsPerMinute[i])
118 int BitPackByte(unsigned char *Dest, int CurrentBit, unsigned char Command, int Bits)
120 unsigned char Byte[]={Command};
122 return BitPack(Dest, CurrentBit, Byte, Bits);
127 /* This is messy but saves using the math library! */
129 int GSM_GetDuration(int number, unsigned char *spec)
136 duration=Duration_Full; *spec=DottedNote; break;
138 duration=Duration_Full; *spec=Length_2_3; break;
140 duration=Duration_Full; *spec=NoSpecialDuration; break;
142 duration=Duration_1_2; *spec=DoubleDottedNote; break;
144 duration=Duration_1_2; *spec=DottedNote; break;
146 duration=Duration_1_2; *spec=Length_2_3; break;
148 duration=Duration_1_2; *spec=NoSpecialDuration; break;
150 duration=Duration_1_4; *spec=DoubleDottedNote; break;
152 duration=Duration_1_4; *spec=DottedNote; break;
154 duration=Duration_1_4; *spec=Length_2_3; break;
156 duration=Duration_1_4; *spec=NoSpecialDuration; break;
158 duration=Duration_1_8; *spec=DoubleDottedNote; break;
160 duration=Duration_1_8; *spec=DottedNote; break;
162 duration=Duration_1_8; *spec=Length_2_3; break;
164 duration=Duration_1_8; *spec=NoSpecialDuration; break;
166 duration=Duration_1_16; *spec=DoubleDottedNote; break;
168 duration=Duration_1_16; *spec=DottedNote; break;
170 duration=Duration_1_16; *spec=Length_2_3; break;
172 duration=Duration_1_16; *spec=NoSpecialDuration; break;
174 duration=Duration_1_32; *spec=DoubleDottedNote; break;
176 duration=Duration_1_32; *spec=DottedNote; break;
178 duration=Duration_1_32; *spec=Length_2_3; break;
180 duration=Duration_1_32; *spec=NoSpecialDuration; break;
187 int GSM_GetNote(int number)
198 note=Note_Cis; break;
202 note=Note_Dis; break;
208 note=Note_Fis; break;
212 note=Note_Gis; break;
216 note=Note_Ais; break;
221 else note = Note_Pause;
227 int GSM_GetScale(int number)
234 /* Ensure the scale is valid */
243 /* This function packs the ringtone from the structure, so it can be set
244 or sent via sms to another phone.
245 Function returns number of packed notes and changes maxlength to
246 number of used chars in "package" */
248 u8 GSM_PackRingtone(GSM_Ringtone *ringtone, char *package, int *maxlength)
252 unsigned char CommandLength = 0x02;
254 int oldscale=10, newscale;
255 int HowMany=0, HowLong=0, StartNote=0, EndNote=0;
257 StartBit=BitPackByte(package, StartBit, CommandLength, 8);
258 StartBit=BitPackByte(package, StartBit, RingingToneProgramming, 7);
260 /* The page 3-23 of the specs says that <command-part> is always
262 StartBit=OctetAlign(package, StartBit);
264 StartBit=BitPackByte(package, StartBit, Sound, 7);
265 StartBit=BitPackByte(package, StartBit, BasicSongType, 3);
267 /* Packing the name of the tune. */
268 StartBit=BitPackByte(package, StartBit, strlen(ringtone->name)<<4, 4);
269 StartBit=BitPack(package, StartBit, ringtone->name, 8*strlen(ringtone->name));
271 /* Info about song pattern */
272 StartBit=BitPackByte(package, StartBit, 0x01, 8); /* One song pattern */
273 StartBit=BitPackByte(package, StartBit, PatternHeaderId, 3);
274 StartBit=BitPackByte(package, StartBit, A_part, 2);
275 StartBit=BitPackByte(package, StartBit, 0, 4); /* No loop value */
277 /* Info, how long is contents for SMS */
278 HowLong=30+8*strlen(ringtone->name)+17+8+8+13;
280 /* Calculate the number of instructions in the tune.
281 Each Note contains Note and (sometimes) Scale.
282 Default Tempo and Style are instructions too. */
283 HowMany=2; /* Default Tempo and Style */
285 for(i=0; i<ringtone->NrNotes; i++) {
287 /* PC Composer 2.0.010 doesn't like, when we start ringtone from pause:
288 it displays that the format is invalid and
289 hangs, when you move mouse over place, where pause is */
290 if (GSM_GetNote(ringtone->notes[i].note)==Note_Pause && oldscale==10) {
294 /* we don't write Scale info before "Pause" note - it saves space */
295 if (GSM_GetNote(ringtone->notes[i].note)!=Note_Pause &&
296 oldscale!=(newscale=GSM_GetScale(ringtone->notes[i].note))) {
298 /* We calculate, if we have space to add next scale instruction */
299 if (((HowLong+5)/8)<=(*maxlength-1)) {
308 /* We calculate, if we have space to add next note instruction */
309 if (((HowLong+12)/8)<=(*maxlength-1)) {
318 /* If we are sure, we pack it for SMS or setting to phone, not for OTT file */
319 if (*maxlength<1000) {
320 /* Pc Composer gives this as the phone limitation */
321 if ((EndNote-StartNote)==GSM_MAX_RINGTONE_NOTES-1) break;
325 StartBit=BitPackByte(package, StartBit, HowMany, 8);
328 StartBit=BitPackByte(package, StartBit, StyleInstructionId, 3);
329 StartBit=BitPackByte(package, StartBit, ContinuousStyle, 2);
331 /* Beats per minute/tempo of the tune */
332 StartBit=BitPackByte(package, StartBit, TempoInstructionId, 3);
333 StartBit=BitPackByte(package, StartBit, GetTempo(ringtone->tempo), 5);
339 for(i=StartNote; i<(EndNote+StartNote); i++) {
341 /* we don't write Scale info before "Pause" note - it saves place */
342 if (GSM_GetNote(ringtone->notes[i].note)!=Note_Pause &&
343 oldscale!=(newscale=GSM_GetScale(ringtone->notes[i].note))) {
345 StartBit=BitPackByte(package, StartBit, ScaleInstructionId, 3);
346 StartBit=BitPackByte(package, StartBit, GSM_GetScale(ringtone->notes[i].note), 2);
350 StartBit=BitPackByte(package, StartBit, NoteInstructionId, 3);
351 StartBit=BitPackByte(package, StartBit, GSM_GetNote(ringtone->notes[i].note), 4);
352 StartBit=BitPackByte(package, StartBit, GSM_GetDuration(ringtone->notes[i].duration,&spec), 3);
353 StartBit=BitPackByte(package, StartBit, spec, 2);
356 StartBit=OctetAlign(package, StartBit);
358 StartBit=BitPackByte(package, StartBit, CommandEnd, 8);
360 if (StartBit!=OctetAlignNumber(HowLong))
361 dprintf("Error in PackRingtone - StartBit different to HowLong %d - %d)\n", StartBit,OctetAlignNumber(HowLong));
363 *maxlength=StartBit/8;
365 return(EndNote+StartNote);
369 int BitUnPack(unsigned char *Dest, int CurrentBit, unsigned char *Source, int Bits)
373 for (i=0; i<Bits; i++)
374 if (GetBit(Dest, CurrentBit+i)) {
380 return CurrentBit+Bits;
383 int BitUnPackInt(unsigned char *Src, int CurrentBit, int *integer, int Bits)
387 for (i=0; i<Bits; i++) {
388 if (GetBit(Src, CurrentBit+i)) l=l+z;
397 int OctetUnAlign(int CurrentBit)
401 while((CurrentBit+i)%8) i++;
407 /* TODO: better checking, if contents of ringtone is OK */
409 GSM_Error GSM_UnPackRingtone(GSM_Ringtone *ringtone, char *package, int maxlength)
412 int spec, duration, scale;
416 StartBit = BitUnPackInt(package, StartBit, &l, 8);
418 dprintf("Not header\n");
419 return GE_SUBFORMATNOTSUPPORTED;
422 StartBit = BitUnPackInt(package, StartBit, &l, 7);
423 if (l != RingingToneProgramming) {
424 dprintf("Not RingingToneProgramming\n");
425 return GE_SUBFORMATNOTSUPPORTED;
428 /* The page 3-23 of the specs says that <command-part> is always
430 StartBit = OctetUnAlign(StartBit);
432 StartBit = BitUnPackInt(package, StartBit, &l, 7);
434 dprintf("Not Sound\n");
435 return GE_SUBFORMATNOTSUPPORTED;
438 StartBit = BitUnPackInt(package, StartBit, &l, 3);
439 if (l != BasicSongType) {
440 dprintf("Not BasicSongType\n");
441 return GE_SUBFORMATNOTSUPPORTED;
444 /* Getting length of the tune name */
445 StartBit = BitUnPackInt(package, StartBit, &l, 4);
448 /* Unpacking the name of the tune. */
449 StartBit = BitUnPack(package, StartBit, ringtone->name, 8*l);
450 ringtone->name[l] = 0;
452 StartBit = BitUnPackInt(package, StartBit, &l, 8);
453 if (l != 1) return GE_SUBFORMATNOTSUPPORTED; //we support only one song pattern
455 StartBit = BitUnPackInt(package, StartBit, &l, 3);
456 if (l != PatternHeaderId) {
457 dprintf("Not PatternHeaderId\n");
458 return GE_SUBFORMATNOTSUPPORTED;
461 StartBit += 2; //Pattern ID - we ignore it
463 StartBit = BitUnPackInt(package, StartBit, &l, 4);
466 StartBit = BitUnPackInt(package, StartBit, &HowMany, 8);
469 ringtone->NrNotes = 0;
471 for (i = 0; i < HowMany; i++) {
473 StartBit = BitUnPackInt(package, StartBit, &q, 3);
475 case VolumeInstructionId:
478 case StyleInstructionId:
479 StartBit = BitUnPackInt(package,StartBit,&l,2);
482 case TempoInstructionId:
483 StartBit = BitUnPackInt(package, StartBit, &l, 5);
485 ringtone->tempo = BeatsPerMinute[l];
487 case ScaleInstructionId:
488 StartBit = BitUnPackInt(package, StartBit, &scale, 2);
491 case NoteInstructionId:
492 StartBit = BitUnPackInt(package, StartBit, &l, 4);
495 case Note_C :ringtone->notes[ringtone->NrNotes].note = 0; break;
496 case Note_Cis :ringtone->notes[ringtone->NrNotes].note = 1; break;
497 case Note_D :ringtone->notes[ringtone->NrNotes].note = 2; break;
498 case Note_Dis :ringtone->notes[ringtone->NrNotes].note = 3; break;
499 case Note_E :ringtone->notes[ringtone->NrNotes].note = 4; break;
500 case Note_F :ringtone->notes[ringtone->NrNotes].note = 6; break;
501 case Note_Fis :ringtone->notes[ringtone->NrNotes].note = 7; break;
502 case Note_G :ringtone->notes[ringtone->NrNotes].note = 8; break;
503 case Note_Gis :ringtone->notes[ringtone->NrNotes].note = 9; break;
504 case Note_A :ringtone->notes[ringtone->NrNotes].note = 10; break;
505 case Note_Ais :ringtone->notes[ringtone->NrNotes].note = 11; break;
506 case Note_H :ringtone->notes[ringtone->NrNotes].note = 12; break;
507 default :ringtone->notes[ringtone->NrNotes].note = 255; break; //Pause ?
510 if (ringtone->notes[ringtone->NrNotes].note != 255)
511 ringtone->notes[ringtone->NrNotes].note = ringtone->notes[ringtone->NrNotes].note + scale*14;
513 StartBit = BitUnPackInt(package, StartBit, &duration, 3);
515 StartBit = BitUnPackInt(package, StartBit, &spec, 2);
517 if (duration==Duration_Full && spec==DottedNote)
518 ringtone->notes[ringtone->NrNotes].duration=128*3/2;
519 if (duration==Duration_Full && spec==Length_2_3)
520 ringtone->notes[ringtone->NrNotes].duration=128*2/3;
521 if (duration==Duration_Full && spec==NoSpecialDuration)
522 ringtone->notes[ringtone->NrNotes].duration=128;
523 if (duration==Duration_1_2 && spec==DottedNote)
524 ringtone->notes[ringtone->NrNotes].duration=64*3/2;
525 if (duration==Duration_1_2 && spec==Length_2_3)
526 ringtone->notes[ringtone->NrNotes].duration=64*2/3;
527 if (duration==Duration_1_2 && spec==NoSpecialDuration)
528 ringtone->notes[ringtone->NrNotes].duration=64;
529 if (duration==Duration_1_4 && spec==DottedNote)
530 ringtone->notes[ringtone->NrNotes].duration=32*3/2;
531 if (duration==Duration_1_4 && spec==Length_2_3)
532 ringtone->notes[ringtone->NrNotes].duration=32*2/3;
533 if (duration==Duration_1_4 && spec==NoSpecialDuration)
534 ringtone->notes[ringtone->NrNotes].duration=32;
535 if (duration==Duration_1_8 && spec==DottedNote)
536 ringtone->notes[ringtone->NrNotes].duration=16*3/2;
537 if (duration==Duration_1_8 && spec==Length_2_3)
538 ringtone->notes[ringtone->NrNotes].duration=16*2/3;
539 if (duration==Duration_1_8 && spec==NoSpecialDuration)
540 ringtone->notes[ringtone->NrNotes].duration=16;
541 if (duration==Duration_1_16 && spec==DottedNote)
542 ringtone->notes[ringtone->NrNotes].duration=8*3/2;
543 if (duration==Duration_1_16 && spec==Length_2_3)
544 ringtone->notes[ringtone->NrNotes].duration=8*2/3;
545 if (duration==Duration_1_16 && spec==NoSpecialDuration)
546 ringtone->notes[ringtone->NrNotes].duration=8;
547 if (duration==Duration_1_32 && spec==DottedNote)
548 ringtone->notes[ringtone->NrNotes].duration=4*3/2;
549 if (duration==Duration_1_32 && spec==Length_2_3)
550 ringtone->notes[ringtone->NrNotes].duration=4*2/3;
551 if (duration==Duration_1_32 && spec==NoSpecialDuration)
552 ringtone->notes[ringtone->NrNotes].duration=4;
554 if (ringtone->NrNotes==MAX_RINGTONE_NOTES) break;
559 dprintf("Unsupported block\n");
560 return GE_SUBFORMATNOTSUPPORTED;
568 GSM_Error GSM_ReadRingtoneFromSMS(GSM_SMSMessage *message, GSM_Ringtone *ringtone)
570 if (message->UDH[0].Type==SMS_Ringtone) {
571 return GSM_UnPackRingtone(ringtone, message->MessageText, message->Length);
572 } else return GE_SUBFORMATNOTSUPPORTED;
576 int GSM_SaveRingtoneToSMS(GSM_SMSMessage *message, GSM_Ringtone *ringtone)
578 int i, j = GSM_MAX_8BIT_SMS_LENGTH;
580 char UserDataHeader[7]= { 0x06, /* User Data Header Length */
581 0x05, /* IEI: application port addressing scheme, 16 bit address */
582 0x04, /* IEDL (IED length ?) */
583 0x15, /* destination address: high byte */
584 0x81, /* destination address: low byte */
585 0x15, /* originator address: high byte */
586 0x81}; /* originator address: low byte */
588 /* Default settings for SMS message:
595 - set UserDataHeaderIndicator
598 message->Type = SMS_Sent;
600 /* Data Coding Scheme */
601 message->DCS.Type = SMS_GeneralDataCoding;
602 message->DCS.u.General.Class = 2;
603 message->DCS.u.General.Compressed = false;
604 message->DCS.u.General.Alphabet = SMS_8bit;
606 message->MessageCenter.No = 1;
607 message->Validity.VPF = SMS_RelativeFormat;
608 message->Validity.u.Relative = 4320; /* 4320 minutes == 72 hours */
609 message->ReplyViaSameSMSC = false;
612 message->UDH[0].Type = SMS_Ringtone;
616 memcpy(message->MessageText, UserDataHeader, 7);
617 i = GSM_PackRingtone(ringtone, message->MessageText + 7, &j);