/* $Id$ G N O K I I A Linux/Unix toolset and driver for Nokia mobile phones. Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml. Released under the terms of the GNU GPL, see file COPYING for more details. This file provides support for ringtones. $Log$ Revision 1.1.1.8 2002/04/03 00:07:57 short Found in "gnokii-working" directory, some November-patches version Revision 1.2 2001/09/20 21:46:21 pkot Locale cleanups (Pawel Kot) */ #include "gsm-ringtones.h" #include "misc.h" /* Beats-per-Minute Encoding */ int BeatsPerMinute[] = { 25, 28, 31, 35, 40, 45, 50, 56, 63, 70, 80, 90, 100, 112, 125, 140, 160, 180, 200, 225, 250, 285, 320, 355, 400, 450, 500, 565, 635, 715, 800, 900 }; int OctetAlign(unsigned char *Dest, int CurrentBit) { int i=0; while((CurrentBit+i)%8) { ClearBit(Dest, CurrentBit+i); i++; } return CurrentBit+i; } int OctetAlignNumber(int CurrentBit) { int i=0; while((CurrentBit+i)%8) { i++; } return CurrentBit+i; } int BitPack(unsigned char *Dest, int CurrentBit, unsigned char *Source, int Bits) { int i; for (i=0; i is always octet-aligned. */ StartBit=OctetAlign(package, StartBit); StartBit=BitPackByte(package, StartBit, Sound, 7); StartBit=BitPackByte(package, StartBit, BasicSongType, 3); /* Packing the name of the tune. */ StartBit=BitPackByte(package, StartBit, strlen(ringtone->name)<<4, 4); StartBit=BitPack(package, StartBit, ringtone->name, 8*strlen(ringtone->name)); /* Info about song pattern */ StartBit=BitPackByte(package, StartBit, 0x01, 8); /* One song pattern */ StartBit=BitPackByte(package, StartBit, PatternHeaderId, 3); StartBit=BitPackByte(package, StartBit, A_part, 2); StartBit=BitPackByte(package, StartBit, 0, 4); /* No loop value */ /* Info, how long is contents for SMS */ HowLong=30+8*strlen(ringtone->name)+17+8+8+13; /* Calculate the number of instructions in the tune. Each Note contains Note and (sometimes) Scale. Default Tempo and Style are instructions too. */ HowMany=2; /* Default Tempo and Style */ for(i=0; iNrNotes; i++) { /* PC Composer 2.0.010 doesn't like, when we start ringtone from pause: it displays that the format is invalid and hangs, when you move mouse over place, where pause is */ if (GSM_GetNote(ringtone->notes[i].note)==Note_Pause && oldscale==10) { StartNote++; } else { /* we don't write Scale info before "Pause" note - it saves space */ if (GSM_GetNote(ringtone->notes[i].note)!=Note_Pause && oldscale!=(newscale=GSM_GetScale(ringtone->notes[i].note))) { /* We calculate, if we have space to add next scale instruction */ if (((HowLong+5)/8)<=(*maxlength-1)) { oldscale=newscale; HowMany++; HowLong+=5; } else { break; } } /* We calculate, if we have space to add next note instruction */ if (((HowLong+12)/8)<=(*maxlength-1)) { HowMany++; EndNote++; HowLong+=12; } else { break; } } /* If we are sure, we pack it for SMS or setting to phone, not for OTT file */ if (*maxlength<1000) { /* Pc Composer gives this as the phone limitation */ if ((EndNote-StartNote)==GSM_MAX_RINGTONE_NOTES-1) break; } } StartBit=BitPackByte(package, StartBit, HowMany, 8); /* Style */ StartBit=BitPackByte(package, StartBit, StyleInstructionId, 3); StartBit=BitPackByte(package, StartBit, ContinuousStyle, 2); /* Beats per minute/tempo of the tune */ StartBit=BitPackByte(package, StartBit, TempoInstructionId, 3); StartBit=BitPackByte(package, StartBit, GetTempo(ringtone->tempo), 5); /* Default scale */ oldscale=10; /* Notes packing */ for(i=StartNote; i<(EndNote+StartNote); i++) { /* we don't write Scale info before "Pause" note - it saves place */ if (GSM_GetNote(ringtone->notes[i].note)!=Note_Pause && oldscale!=(newscale=GSM_GetScale(ringtone->notes[i].note))) { oldscale=newscale; StartBit=BitPackByte(package, StartBit, ScaleInstructionId, 3); StartBit=BitPackByte(package, StartBit, GSM_GetScale(ringtone->notes[i].note), 2); } /* Note */ StartBit=BitPackByte(package, StartBit, NoteInstructionId, 3); StartBit=BitPackByte(package, StartBit, GSM_GetNote(ringtone->notes[i].note), 4); StartBit=BitPackByte(package, StartBit, GSM_GetDuration(ringtone->notes[i].duration,&spec), 3); StartBit=BitPackByte(package, StartBit, spec, 2); } StartBit=OctetAlign(package, StartBit); StartBit=BitPackByte(package, StartBit, CommandEnd, 8); if (StartBit!=OctetAlignNumber(HowLong)) dprintf("Error in PackRingtone - StartBit different to HowLong %d - %d)\n", StartBit,OctetAlignNumber(HowLong)); *maxlength=StartBit/8; return(EndNote+StartNote); } int BitUnPack(unsigned char *Dest, int CurrentBit, unsigned char *Source, int Bits) { int i; for (i=0; i is always octet-aligned. */ StartBit=OctetUnAlign(StartBit); StartBit=BitUnPackInt(package,StartBit,&l,7); if (l!=Sound) { dprintf("Not Sound\n"); return GE_SUBFORMATNOTSUPPORTED; } StartBit=BitUnPackInt(package,StartBit,&l,3); if (l!=BasicSongType) { dprintf("Not BasicSongType\n"); return GE_SUBFORMATNOTSUPPORTED; } /* Getting length of the tune name */ StartBit=BitUnPackInt(package,StartBit,&l,4); l=l>>4; /* Unpacking the name of the tune. */ StartBit=BitUnPack(package, StartBit, ringtone->name, 8*l); ringtone->name[l]=0; StartBit=BitUnPackInt(package,StartBit,&l,8); if (l!=1) return GE_SUBFORMATNOTSUPPORTED; //we support only one song pattern StartBit=BitUnPackInt(package,StartBit,&l,3); if (l!=PatternHeaderId) { dprintf("Not PatternHeaderId\n"); return GE_SUBFORMATNOTSUPPORTED; } StartBit+=2; //Pattern ID - we ignore it StartBit=BitUnPackInt(package,StartBit,&l,4); HowMany=0; StartBit=BitUnPackInt(package, StartBit, &HowMany, 8); scale=0; ringtone->NrNotes=0; for (i=0;i>3; break; case TempoInstructionId: StartBit=BitUnPackInt(package,StartBit,&l,5); l=l>>3; ringtone->tempo=BeatsPerMinute[l]; break; case ScaleInstructionId: StartBit=BitUnPackInt(package,StartBit,&scale,2); scale=scale>>6; break; case NoteInstructionId: StartBit=BitUnPackInt(package,StartBit,&l,4); switch (l) { case Note_C :ringtone->notes[ringtone->NrNotes].note=0;break; case Note_Cis:ringtone->notes[ringtone->NrNotes].note=1;break; case Note_D :ringtone->notes[ringtone->NrNotes].note=2;break; case Note_Dis:ringtone->notes[ringtone->NrNotes].note=3;break; case Note_E :ringtone->notes[ringtone->NrNotes].note=4;break; case Note_F :ringtone->notes[ringtone->NrNotes].note=6;break; case Note_Fis:ringtone->notes[ringtone->NrNotes].note=7;break; case Note_G :ringtone->notes[ringtone->NrNotes].note=8;break; case Note_Gis:ringtone->notes[ringtone->NrNotes].note=9;break; case Note_A :ringtone->notes[ringtone->NrNotes].note=10;break; case Note_Ais:ringtone->notes[ringtone->NrNotes].note=11;break; case Note_H :ringtone->notes[ringtone->NrNotes].note=12;break; default :ringtone->notes[ringtone->NrNotes].note=255;break; //Pause ? } if (ringtone->notes[ringtone->NrNotes].note!=255) ringtone->notes[ringtone->NrNotes].note=ringtone->notes[ringtone->NrNotes].note+scale*14; StartBit=BitUnPackInt(package,StartBit,&duration,3); StartBit=BitUnPackInt(package,StartBit,&spec,2); if (duration==Duration_Full && spec==DottedNote) ringtone->notes[ringtone->NrNotes].duration=128*3/2; if (duration==Duration_Full && spec==Length_2_3) ringtone->notes[ringtone->NrNotes].duration=128*2/3; if (duration==Duration_Full && spec==NoSpecialDuration) ringtone->notes[ringtone->NrNotes].duration=128; if (duration==Duration_1_2 && spec==DottedNote) ringtone->notes[ringtone->NrNotes].duration=64*3/2; if (duration==Duration_1_2 && spec==Length_2_3) ringtone->notes[ringtone->NrNotes].duration=64*2/3; if (duration==Duration_1_2 && spec==NoSpecialDuration) ringtone->notes[ringtone->NrNotes].duration=64; if (duration==Duration_1_4 && spec==DottedNote) ringtone->notes[ringtone->NrNotes].duration=32*3/2; if (duration==Duration_1_4 && spec==Length_2_3) ringtone->notes[ringtone->NrNotes].duration=32*2/3; if (duration==Duration_1_4 && spec==NoSpecialDuration) ringtone->notes[ringtone->NrNotes].duration=32; if (duration==Duration_1_8 && spec==DottedNote) ringtone->notes[ringtone->NrNotes].duration=16*3/2; if (duration==Duration_1_8 && spec==Length_2_3) ringtone->notes[ringtone->NrNotes].duration=16*2/3; if (duration==Duration_1_8 && spec==NoSpecialDuration) ringtone->notes[ringtone->NrNotes].duration=16; if (duration==Duration_1_16 && spec==DottedNote) ringtone->notes[ringtone->NrNotes].duration=8*3/2; if (duration==Duration_1_16 && spec==Length_2_3) ringtone->notes[ringtone->NrNotes].duration=8*2/3; if (duration==Duration_1_16 && spec==NoSpecialDuration) ringtone->notes[ringtone->NrNotes].duration=8; if (duration==Duration_1_32 && spec==DottedNote) ringtone->notes[ringtone->NrNotes].duration=4*3/2; if (duration==Duration_1_32 && spec==Length_2_3) ringtone->notes[ringtone->NrNotes].duration=4*2/3; if (duration==Duration_1_32 && spec==NoSpecialDuration) ringtone->notes[ringtone->NrNotes].duration=4; if (ringtone->NrNotes==MAX_RINGTONE_NOTES) break; ringtone->NrNotes++; break; default: dprintf("Unsupported block\n"); return GE_SUBFORMATNOTSUPPORTED; } } return GE_NONE; } #if 0 /* unused */ GSM_Error GSM_ReadRingtoneFromSMS(GSM_SMSMessage *message, GSM_Ringtone *ringtone) { if (message->UDHType==GSM_RingtoneUDH) { return GSM_UnPackRingtone(ringtone, message->MessageText, message->Length); } else return GE_SUBFORMATNOTSUPPORTED; } #endif /* unused */ #if 0 /* unused */ int GSM_SaveRingtoneToSMS(GSM_SMSMessage *message, GSM_Ringtone *ringtone) { int i, j=GSM_MAX_SMS_8_BIT_LENGTH; char UserDataHeader[7]= { 0x06, /* User Data Header Length */ 0x05, /* IEI: application port addressing scheme, 16 bit address */ 0x04, /* IEDL (IED length ?) */ 0x15, /* destination address: high byte */ 0x81, /* destination address: low byte */ 0x15, /* originator address: high byte */ 0x81}; /* originator address: low byte */ /* Default settings for SMS message: - no delivery report - Class Message 1 - no compression - 8 bit data - SMSC no. 1 - validity 3 days - set UserDataHeaderIndicator */ message->Type = GST_MO; message->Class = 1; message->Compression = false; message->EightBit = true; message->MessageCenter.No = 1; message->Validity = 4320; /* 4320 minutes == 72 hours */ message->ReplyViaSameSMSC = false; message->UDHType = GSM_RingtoneUDH; i=GSM_PackRingtone(ringtone, message->MessageText, &j); message->Length=j; memcpy(message->UDH,UserDataHeader,7); return i; } #endif /* unused */