/* $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.1 2001/11/25 21:59:00 short :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001 Revision 1.3 2001/11/08 16:34:19 pkot Updates to work with new libsms 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 < HowMany; i++) { StartBit = BitUnPackInt(package, StartBit, &q, 3); switch (q) { case VolumeInstructionId: StartBit += 4; break; case StyleInstructionId: StartBit = BitUnPackInt(package,StartBit,&l,2); l = l >> 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; } GSM_Error GSM_ReadRingtoneFromSMS(GSM_SMSMessage *message, GSM_Ringtone *ringtone) { if (message->UDH[0].Type==SMS_Ringtone) { return GSM_UnPackRingtone(ringtone, message->MessageText, message->Length); } else return GE_SUBFORMATNOTSUPPORTED; } int GSM_SaveRingtoneToSMS(GSM_SMSMessage *message, GSM_Ringtone *ringtone) { int i, j = GSM_MAX_8BIT_SMS_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 = SMS_Sent; /* Data Coding Scheme */ message->DCS.Type = SMS_GeneralDataCoding; message->DCS.u.General.Class = 2; message->DCS.u.General.Compressed = false; message->DCS.u.General.Alphabet = SMS_8bit; message->MessageCenter.No = 1; message->Validity.VPF = SMS_RelativeFormat; message->Validity.u.Relative = 4320; /* 4320 minutes == 72 hours */ message->ReplyViaSameSMSC = false; message->UDH_No = 1; message->UDH[0].Type = SMS_Ringtone; message->Length = j; memcpy(message->MessageText, UserDataHeader, 7); i = GSM_PackRingtone(ringtone, message->MessageText + 7, &j); return i; }