author | zautrix <zautrix> | 2004-08-07 17:24:40 (UTC) |
---|---|---|
committer | zautrix <zautrix> | 2004-08-07 17:24:40 (UTC) |
commit | 88b0d33b8b0b1f6ae320cfc863ca6a47fa8fec22 (patch) (side-by-side diff) | |
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) (ignore 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 @@ +/* (c) 2001-2004 by Marcin Wiacek */ +/* Based on some work from Ralf Thelen (7110 ringtones), + * Gnokii (RTTL and SM) and others + */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#ifdef WIN32 +# include <windows.h> +#endif + +#include "../gsmcomon.h" +#include "../misc/coding/coding.h" +#include "../gsmstate.h" +#include "gsmring.h" +#include "sms/gsmsms.h" + +int GSM_RingNoteGetFrequency(GSM_RingNote Note) +{ + double freq=0; + + /* Values according to the software from http://iki.fi/too/sw/xring/ + * generated with: + * perl -e 'print int(4400 * (2 **($_/12)) + .5)/10, "\n" for(3..14)' + */ + switch (Note.Note) { + case Note_C : freq = 523.3; break; + case Note_Cis: freq = 554.4; break; + case Note_D : freq = 587.3; break; + case Note_Dis: freq = 622.3; break; + case Note_E : freq = 659.3; break; + case Note_F : freq = 698.5; break; + case Note_Fis: freq = 740; break; + case Note_G : freq = 784; break; + case Note_Gis: freq = 830.6; break; + case Note_A : freq = 880; break; + case Note_Ais: freq = 932.3; break; + case Note_H : freq = 987.8; break; + case Note_Pause: break; + } + switch (Note.Scale) { + case Scale_440 : freq = freq / 2; break; + case Scale_880 : break; + case Scale_1760: freq = freq * 2; break; + case Scale_3520: freq = freq * 4; break; + default : break; + } + return (int)freq; +} + +int GSM_RingNoteGetFullDuration(GSM_RingNote Note) +{ + int duration = 1; + + switch (Note.Duration) { + case Duration_Full : duration = 128; break; + case Duration_1_2 : duration = 64; break; + case Duration_1_4 : duration = 32; break; + case Duration_1_8 : duration = 16; break; + case Duration_1_16 : duration = 8; break; + case Duration_1_32 : duration = 4; break; + } + switch (Note.DurationSpec) { + case NoSpecialDuration : break; + case DottedNote : duration = duration * 3/2; break; + case DoubleDottedNote : duration = duration * 9/4; break; + case Length_2_3 : duration = duration * 2/3; break; + } + return duration; +} + +#ifndef PI +# define PI 3.141592654 +#endif + +#define WAV_SAMPLE_RATE 44100 + +GSM_Error savewav(FILE *file, GSM_Ringtone *ringtone) +{ + unsigned char WAV_Header[] = { + 'R','I','F','F', + 0x00,0x00,0x00,0x00, /* Length */ + 'W','A','V','E'}; + unsigned char FMT_Header[] = {'f','m','t',' ', + 0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x44,0xac, + 0x00,0x00,0x88,0x58,0x01,0x00,0x02,0x00,0x10,0x00}; + unsigned char DATA_Header[] = { + 'd','a','t','a', + 0x00,0x00,0x00,0x00}; /* Length */ + short DATA_Buffer[60000]; + long wavfilesize; + GSM_RingNote *Note; + long i,j,length=0; + double phase=0,phase_step; + + fwrite(&WAV_Header, 1, sizeof(WAV_Header), file); + fwrite(&FMT_Header, 1, sizeof(FMT_Header), file); + fwrite(&DATA_Header, 1, sizeof(DATA_Header), file); + + for (i=0;i<ringtone->NoteTone.NrCommands;i++) { + if (ringtone->NoteTone.Commands[i].Type == RING_Note) { + Note = &ringtone->NoteTone.Commands[i].Note; + phase_step = GSM_RingNoteGetFrequency(*Note)*WAV_SAMPLE_RATE*1.5; + for (j=0;j<((long)(GSM_RingNoteGetFullDuration(*Note)*WAV_SAMPLE_RATE/70));j++) { + DATA_Buffer[j] = ((int)(sin(phase*PI)*50000)); + phase = phase + phase_step; + length++; + } + fwrite(&DATA_Buffer,sizeof(short),j,file); + } + } + + wavfilesize = sizeof(WAV_Header) + sizeof(FMT_Header) + sizeof(DATA_Header) + length*2; + WAV_Header[4] = ((unsigned char)wavfilesize % 256); + WAV_Header[5] = ((unsigned char)wavfilesize / 256); + WAV_Header[6] = ((unsigned char)wavfilesize / (256*256)); + WAV_Header[7] = ((unsigned char)wavfilesize / (256*256*256)); + wavfilesize = wavfilesize - 54; + DATA_Header[4] = ((unsigned char)wavfilesize % 256); + DATA_Header[5] = ((unsigned char)wavfilesize / 256); + DATA_Header[6] = ((unsigned char)wavfilesize / (256*256)); + DATA_Header[7] = ((unsigned char)wavfilesize / (256*256*256)); + + fseek( file, 0, SEEK_SET); + fwrite(&WAV_Header, 1, sizeof(WAV_Header), file); + fwrite(&FMT_Header, 1, sizeof(FMT_Header), file); + fwrite(&DATA_Header, 1, sizeof(DATA_Header), file); + + return ERR_NONE; +} + +static GSM_Error savebin(FILE *file, GSM_Ringtone *ringtone) +{ + char nullchar=0x00; + + fwrite(&nullchar,1,1,file); + fwrite(&nullchar,1,1,file); + fprintf(file,"\x0C\x01\x2C"); + fprintf(file,"%s",DecodeUnicodeString(ringtone->Name)); + fwrite(&nullchar,1,1,file); + fwrite(&nullchar,1,1,file); + fwrite(ringtone->NokiaBinary.Frame,1,ringtone->NokiaBinary.Length,file); + return ERR_NONE; +} + +static GSM_Error savepuremidi(FILE *file, GSM_Ringtone *ringtone) +{ + fwrite(ringtone->NokiaBinary.Frame,1,ringtone->NokiaBinary.Length,file); + return ERR_NONE; +} + +GSM_Error saverttl(FILE *file, GSM_Ringtone *ringtone) +{ + GSM_RingNoteScale DefNoteScale; + GSM_RingNoteDuration DefNoteDuration; + + GSM_RingNoteStyle DefNoteStyle=0; + int DefNoteTempo=0; + + bool started = false, firstcomma = true; + GSM_RingNote *Note; + + unsigned char buffer[15]; + int i,j,k=0; + + /* Saves ringtone name */ + fprintf(file,"%s:",DecodeUnicodeString(ringtone->Name)); + + /* Find the most frequently used duration */ + for (i=0;i<6;i++) buffer[i]=0; + for (i=0;i<ringtone->NoteTone.NrCommands;i++) { + if (ringtone->NoteTone.Commands[i].Type == RING_Note) { + Note = &ringtone->NoteTone.Commands[i].Note; + /* some durations need 2 bytes in file, some 1 */ + if (Note->Duration >= Duration_Full && Note->Duration <= Duration_1_8) { + buffer[Note->Duration/32]++; + } + if (Note->Duration >= Duration_1_16 && Note->Duration <= Duration_1_32) { + buffer[Note->Duration/32]+=2; + } + } + } + /* Now find the most frequently used */ + j=0; + for (i=0;i<6;i++) { + if (buffer[i]>j) { + k=i; + j=buffer[i]; + } + } + /* Finally convert the default duration */ + DefNoteDuration = k * 32; + dbgprintf("DefNoteDuration=%d\n", DefNoteDuration); + switch (DefNoteDuration) { + case Duration_Full:fprintf(file,"d=1"); break; + case Duration_1_2 :fprintf(file,"d=2"); break; + case Duration_1_4 :fprintf(file,"d=4"); break; + case Duration_1_8 :fprintf(file,"d=8"); break; + case Duration_1_16:fprintf(file,"d=16");break; + case Duration_1_32:fprintf(file,"d=32");break; + } + + /* Find the most frequently used scale */ + for (i=0;i<9;i++) buffer[i]=0; + for (i=0;i<ringtone->NoteTone.NrCommands;i++) { + if (ringtone->NoteTone.Commands[i].Type == RING_Note) { + Note = &ringtone->NoteTone.Commands[i].Note; + if (Note->Note!=Note_Pause && + Note->Scale >= Scale_55 && Note->Scale <= Scale_14080) { + buffer[Note->Scale - 1]++; + } + } + } + j=0; + for (i=0;i<9;i++) { + if (buffer[i]>j) { + k = i; + j=buffer[i]; + } + } + DefNoteScale = k + 1; + /* Save the default scale */ + fprintf(file,",o=%i,",DefNoteScale); + dbgprintf("DefNoteScale=%d\n", DefNoteScale); + + for (i=0;i<ringtone->NoteTone.NrCommands;i++) { + if (ringtone->NoteTone.Commands[i].Type != RING_Note) continue; + + Note = &ringtone->NoteTone.Commands[i].Note; + + /* Trick from PPM Edit */ + if (Note->DurationSpec == DoubleDottedNote) { + switch (Note->Duration) { + case Duration_Full:Note->Duration = Duration_Full;break; + case Duration_1_2 :Note->Duration = Duration_Full;break; + case Duration_1_4 :Note->Duration = Duration_1_2; break; + case Duration_1_8 :Note->Duration = Duration_1_4; break; + case Duration_1_16:Note->Duration = Duration_1_8; break; + case Duration_1_32:Note->Duration = Duration_1_16;break; + } + Note->DurationSpec = NoSpecialDuration; + } + + if (!started) { + DefNoteTempo=Note->Tempo; + DefNoteStyle=Note->Style; + switch (Note->Style) { + case StaccatoStyle : fprintf(file,"s=S,"); break; + case NaturalStyle : fprintf(file,"s=N,"); break; + case ContinuousStyle : break; + } + /* Save the default tempo */ + fprintf(file,"b=%i:",DefNoteTempo); + dbgprintf("DefNoteTempo=%d\n", DefNoteTempo); + started = true; + firstcomma = true; + } + + if (!started) continue; + + if (Note->Style!=DefNoteStyle) { + /* And a separator */ + if (!firstcomma) fprintf(file,","); + firstcomma = false; + DefNoteStyle=Note->Style; + switch (Note->Style) { + case StaccatoStyle : fprintf(file,"s=S"); break; + case NaturalStyle : fprintf(file,"s=N"); break; + case ContinuousStyle: fprintf(file,"s=C"); break; + } + } + if (Note->Tempo!=DefNoteTempo) { + /* And a separator */ + if (!firstcomma) fprintf(file,","); + firstcomma = false; + DefNoteTempo=Note->Tempo; + fprintf(file,"b=%i",DefNoteTempo); + } + /* This note has a duration different than the default. We must save it */ + if (Note->Duration!=DefNoteDuration) { + /* And a separator */ + if (!firstcomma) fprintf(file,","); + firstcomma = false; + switch (Note->Duration) { + case Duration_Full:fprintf(file,"1"); break; + case Duration_1_2 :fprintf(file,"2"); break; + case Duration_1_4 :fprintf(file,"4"); break; + case Duration_1_8 :fprintf(file,"8"); break; + case Duration_1_16:fprintf(file,"16");break; + case Duration_1_32:fprintf(file,"32");break; + } + } else { + /* And a separator */ + if (!firstcomma) fprintf(file,","); + firstcomma = false; + } + /* Now save the actual note */ + switch (Note->Note) { + case Note_C :fprintf(file,"c"); break; + case Note_Cis:fprintf(file,"c#"); break; + case Note_D :fprintf(file,"d"); break; + case Note_Dis:fprintf(file,"d#"); break; + case Note_E :fprintf(file,"e"); break; + case Note_F :fprintf(file,"f"); break; + case Note_Fis:fprintf(file,"f#"); break; + case Note_G :fprintf(file,"g"); break; + case Note_Gis:fprintf(file,"g#"); break; + case Note_A :fprintf(file,"a"); break; + case Note_Ais:fprintf(file,"a#"); break; + case Note_H :fprintf(file,"h"); break; + default :fprintf(file,"p"); break; /*Pause ?*/ + } + switch (Note->DurationSpec) { + case DottedNote : fprintf(file,"."); break; + default : break; + } + if (Note->Note!=Note_Pause && Note->Scale != DefNoteScale) { + fprintf(file,"%i",Note->Scale); + } + } + return ERR_NONE; +} + +void saveimelody(FILE *file, GSM_Ringtone *ringtone) +{ + char Buffer[2000]; + int i=2000; + + GSM_EncodeEMSSound(*ringtone, Buffer, &i, (float)1.2, true); + + fwrite(Buffer, 1, i, file); +} + +#ifndef ENABLE_LGPL + +static void WriteVarLen(unsigned char* midifile, int* current, long value) +{ + long buffer; + + buffer = value & 0x7f; + + while (value >>= 7) { + buffer <<= 8; + buffer |= 0x80; + buffer += (value & 0x7f); + } + + while (1) { + midifile[(*current)++] = (unsigned char)buffer; + if (buffer & 0x80) { + buffer >>= 8; + } else { + break; + } + } +} + +#define singlepauses + +/* FIXME: need adding tempo before each note and scale too ? */ +void savemid(FILE* file, GSM_Ringtone *ringtone) +{ + int pause = 0, current = 26, duration, i, note=0, length = 20; + bool started = false; + GSM_RingNote *Note; + unsigned char midifile[3000] = { + 0x4D, 0x54, 0x68, 0x64, // MThd + 0x00, 0x00, 0x00, 0x06, // chunk length + 0x00, 0x00, // format 0 + 0x00, 0x01, // one track + 0x00, 0x20, // 32 per quarter note + 0x4D, 0x54, 0x72, 0x6B, // MTrk + 0x00, 0x00, 0x00, 0x00, // chunk length + 0x00, 0xFF, 0x51, 0x03, // tempo meta event + 0x00, 0x00, 0x00}; // 3 bytes for us for a quarter note + + for (i = 0; i < ringtone->NoteTone.NrCommands; i++) { + if (ringtone->NoteTone.Commands[i].Type == RING_Note) { + Note = &ringtone->NoteTone.Commands[i].Note; + if (!started) { + /* readmid does not read pauses at the beginning */ + if (Note->Note != Note_Pause) { + /* FIXME: we need add tempo before each note or so... */ + long duration=60000000/Note->Tempo; + + midifile[current++] = (unsigned char)(duration >> 16); + midifile[current++] = (unsigned char)(duration >> 8); + midifile[current++] = (unsigned char)duration; + + started = true; + } + } + if (!started) continue; + duration = GSM_RingNoteGetFullDuration(*Note); + if (Note->Note == Note_Pause) { + pause += duration; +#ifdef singlepauses + WriteVarLen(midifile,¤t,pause); + pause=0; + midifile[current++]=0x00; // pause + midifile[current++]=0x00; +#endif + } else { + if (Note->Note >= Note_C && Note->Note <= Note_H) { + note = Note->Note/16 + 12 * Note->Scale - 1; + } + + WriteVarLen(midifile,¤t,pause); + pause=0; + midifile[current++]=0x90; // note on + midifile[current++]=note; + midifile[current++]=0x64; // forte + + WriteVarLen(midifile,¤t,duration); + midifile[current++]=0x80; // note off + midifile[current++]=note; + midifile[current++]=0x64; + } + } + } + if (pause) { + WriteVarLen(midifile,¤t,pause); + midifile[current++]=0x00; // pause + midifile[current++]=0x00; // + } + midifile[current++] = 0x00; + midifile[current++] = 0xFF; // track end + midifile[current++] = 0x2F; + midifile[current++] = 0x00; + midifile[length++] = (current-22) >> 8; + midifile[length++] = current-22; + + fwrite(midifile,1,current,file); +} + +#endif + +void saveott(FILE *file, GSM_Ringtone *ringtone) +{ + char Buffer[2000]; + int i=2000; + + GSM_EncodeNokiaRTTLRingtone(*ringtone, Buffer, &i); + + fwrite(Buffer, 1, i, file); +} + +GSM_Error GSM_SaveRingtoneFile(char *FileName, GSM_Ringtone *ringtone) +{ + FILE *file; + + file = fopen(FileName, "wb"); + if (file == NULL) return ERR_CANTOPENFILE; + + switch (ringtone->Format) { + case RING_NOTETONE: + if (strstr(FileName,".ott")) { + saveott(file,ringtone); +#ifndef ENABLE_LGPL + } else if (strstr(FileName,".mid")) { + savemid(file,ringtone); +#endif + } else if (strstr(FileName,".rng")) { + saveott(file,ringtone); + } else if (strstr(FileName,".imy")) { + saveimelody(file,ringtone); + } else if (strstr(FileName,".ime")) { + saveimelody(file,ringtone); + } else if (strstr(FileName,".wav")) { + savewav(file,ringtone); + } else { + saverttl(file, ringtone); + } + break; + case RING_NOKIABINARY: + savebin(file, ringtone); + break; + case RING_MIDI: + savepuremidi(file, ringtone); + break; + } + + fclose(file); + + return ERR_NONE; +} + +static GSM_Error loadrttl(FILE *file, GSM_Ringtone *ringtone) +{ + GSM_RingNoteScale DefNoteScale = Scale_880; + GSM_RingNoteDuration DefNoteDuration = Duration_1_4; + GSM_RingNoteStyle DefNoteStyle = NaturalStyle; + int DefNoteTempo = 63, i=0; + + unsigned char buffer[2000],Name[100]; + GSM_RingNote *Note; + + fread(buffer, 2000, 1, file); + + ringtone->NoteTone.NrCommands = 0; + + /* -------------- name ---------------- */ + while (buffer[i] != ':') { + if (buffer[i] == 0x00) return ERR_NONE; + i++; + } + if (i == 0) { + /* This is for RTTL ringtones without name. */ + EncodeUnicode(ringtone->Name,"Gammu",5); + } else { + memcpy(Name,buffer,i); + Name[i] = 0x00; + EncodeUnicode(ringtone->Name,Name,strlen(Name)); + } + i++; + + /* --------- section with default ringtone settings ----------- */ + while(1) { + switch (buffer[i]) { + case ':': + break; + case 0x00: + return ERR_NONE; + case 'd': case 'D': + switch (atoi(buffer+i+2)) { + case 1: DefNoteDuration = Duration_Full; break; + case 2: DefNoteDuration = Duration_1_2 ; break; + case 4: DefNoteDuration = Duration_1_4 ; break; + case 8: DefNoteDuration = Duration_1_8 ; break; + case 16: DefNoteDuration = Duration_1_16; break; + case 32: DefNoteDuration = Duration_1_32; break; + } + break; + case 'o': case 'O': + switch (atoi(buffer+i+2)) { + case 4: DefNoteScale = Scale_440 ; break; + case 5: DefNoteScale = Scale_880 ; break; + case 6: DefNoteScale = Scale_1760; break; + case 7: DefNoteScale = Scale_3520; break; + } + break; + case 'b': case 'B': + DefNoteTempo=atoi(buffer+i+2); + dbgprintf("Tempo = %i\n",DefNoteTempo); + break; + case 's': case 'S': + switch (buffer[i+1]) { + case 'C': case 'c': DefNoteStyle=ContinuousStyle; break; + case 'N': case 'n': DefNoteStyle=NaturalStyle; break; + case 'S': case 's': DefNoteStyle=StaccatoStyle; break; + } + switch (buffer[i+2]) { + case 'C': case 'c': DefNoteStyle=ContinuousStyle; break; + case 'N': case 'n': DefNoteStyle=NaturalStyle; break; + case 'S': case 's': DefNoteStyle=StaccatoStyle; break; + } + break; + } + while (buffer[i] != ':' && buffer[i] != ',') { + if (buffer[i] == 0x00) return ERR_NONE; + i++; + } + if (buffer[i] == ',') i++; + if (buffer[i] == ':') break; + } + dbgprintf("DefNoteDuration=%d\n", DefNoteDuration); + dbgprintf("DefNoteScale=%d\n", DefNoteScale); + i++; + + /* ------------------------- notes ------------------------------ */ + while (buffer[i] != 0x00 && ringtone->NoteTone.NrCommands != MAX_RINGTONE_NOTES) { + switch(buffer[i]) { + case 'z': case 'Z': + switch (buffer[i+1]) { + case 'd': + ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableLED; + ringtone->NoteTone.NrCommands++; + break; + case 'D': + ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableLED; + ringtone->NoteTone.NrCommands++; + break; + case 'v': + ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableVibra; + ringtone->NoteTone.NrCommands++; + break; + case 'V': + ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableVibra; + ringtone->NoteTone.NrCommands++; + break; + case 'l': + ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_DisableLight; + ringtone->NoteTone.NrCommands++; + break; + case 'L': + ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_EnableLight; + ringtone->NoteTone.NrCommands++; + } + break; + case 'o': case 'O': + switch (buffer[i+2]) { + case 4: DefNoteScale = Scale_440 ; break; + case 5: DefNoteScale = Scale_880 ; break; + case 6: DefNoteScale = Scale_1760; break; + case 7: DefNoteScale = Scale_3520; break; + } + break; + case 's': case 'S': + switch (buffer[i+1]) { + case 'C': case 'c': DefNoteStyle=ContinuousStyle; break; + case 'N': case 'n': DefNoteStyle=NaturalStyle; break; + case 'S': case 's': DefNoteStyle=StaccatoStyle; break; + } + switch (buffer[i+2]) { + case 'C': case 'c': DefNoteStyle=ContinuousStyle; break; + case 'N': case 'n': DefNoteStyle=NaturalStyle; break; + case 'S': case 's': DefNoteStyle=StaccatoStyle; break; + } + break; + default: + ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_Note; + Note = &ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Note; + Note->Style = DefNoteStyle; + Note->Tempo = DefNoteTempo; + Note->Scale = DefNoteScale; + Note->Duration = DefNoteDuration; + Note->DurationSpec = NoSpecialDuration; + Note->Note = Note_Pause; + + /* Duration */ + switch (atoi(buffer+i)) { + case 1: Note->Duration = Duration_Full ; break; + case 2: Note->Duration = Duration_1_2 ; break; + case 4: Note->Duration = Duration_1_4 ; break; + case 8: Note->Duration = Duration_1_8 ; break; + case 16: Note->Duration = Duration_1_16 ; break; + case 32: Note->Duration = Duration_1_32 ; break; + } + /* We skip all numbers from duration specification */ + while(isdigit(buffer[i])) i++; + + /* Some files can have special duration here */ + if (buffer[i]=='.') { + Note->DurationSpec = DottedNote; + i++; + } + + /* Note */ + /* B or b is not in specs, but I decided to put it, because + * it's in some RTTL files. It's the same to H note */ + switch (buffer[i]) { + case 'A': case 'a': Note->Note = Note_A; break; + case 'B': case 'b': Note->Note = Note_H; break; + case 'C': case 'c': Note->Note = Note_C; break; + case 'D': case 'd': Note->Note = Note_D; break; + case 'E': case 'e': Note->Note = Note_E; break; + case 'F': case 'f': Note->Note = Note_F; break; + case 'G': case 'g': Note->Note = Note_G; break; + case 'H': case 'h': Note->Note = Note_H; break; + } + i++; + + if (buffer[i]=='#') { + switch (Note->Note) { + case Note_A : Note->Note = Note_Ais; break; + case Note_C : Note->Note = Note_Cis; break; + case Note_D : Note->Note = Note_Dis; break; + case Note_F : Note->Note = Note_Fis; break; + case Note_G : Note->Note = Note_Gis; break; + default : break; + } + i++; + } + + /* Some files can have special duration here */ + if (buffer[i]=='.') { + Note->DurationSpec = DottedNote; + i++; + } + + /* Scale */ + if (Note->Note!=Note_Pause && isdigit(buffer[i])) { + switch (atoi(buffer+i)) { + case 4: Note->Scale = Scale_440 ; break; + case 5: Note->Scale = Scale_880 ; break; + case 6: Note->Scale = Scale_1760; break; + case 7: Note->Scale = Scale_3520; break; + } + i++; + } + + ringtone->NoteTone.NrCommands++; + break; + } + while (buffer[i] != ',') { + if (buffer[i] == 0x00) return ERR_NONE; + i++; + } + if (buffer[i] == ',') i++; + } + + return ERR_NONE; +} + +static GSM_Error loadott(FILE *file, GSM_Ringtone *ringtone) +{ + char Buffer[2000]; + int i; + + i=fread(Buffer, 1, 2000, file); + + return GSM_DecodeNokiaRTTLRingtone(ringtone, Buffer, i); +} + +static GSM_Error loadcommunicator(FILE *file, GSM_Ringtone *ringtone) +{ + char Buffer[4000]; + int i,j; + + i=fread(Buffer, 1, 4000, file); + + i=0;j=0; + while (true) { + if (Buffer[j] ==0x00 && Buffer[j+1]==0x02 && + Buffer[j+2]==0x4a && Buffer[j+3]==0x3a) break; + if (j==i-4) return ERR_UNKNOWN; + j++; + } + j++; + + return GSM_DecodeNokiaRTTLRingtone(ringtone, Buffer+j, i-j); +} + +static GSM_Error loadbin(FILE *file, GSM_Ringtone *ringtone) +{ + int i; + unsigned char buffer[2000]; + + dbgprintf("loading binary\n"); + ringtone->NokiaBinary.Length=fread(buffer, 1, 500, file); + i=5; + while (buffer[i]!=0x00) i++; + EncodeUnicode(ringtone->Name,buffer+5,i-5); + while (buffer[i]!=0x02 && buffer[i+1]!=0xFC && buffer[i+2]!=0x09) { + i++; + } + ringtone->NokiaBinary.Length=ringtone->NokiaBinary.Length-i; + memcpy(ringtone->NokiaBinary.Frame,buffer+i,ringtone->NokiaBinary.Length); + dbgprintf("Length %i name \"%s\"\n",ringtone->NokiaBinary.Length,DecodeUnicodeString(ringtone->Name)); + return ERR_NONE; +} + +static GSM_Error loadpuremidi(FILE *file, GSM_Ringtone *ringtone) +{ + unsigned char buffer[30000]; + + dbgprintf("loading midi\n"); + EncodeUnicode(ringtone->Name,"MIDI",4); + ringtone->NokiaBinary.Length=fread(buffer, 1, 30000, file); + memcpy(ringtone->NokiaBinary.Frame,buffer,ringtone->NokiaBinary.Length); + dbgprintf("Length %i name \"%s\"\n",ringtone->NokiaBinary.Length,DecodeUnicodeString(ringtone->Name)); + return ERR_NONE; +} + +static GSM_Error loadre(FILE *file, GSM_Ringtone *ringtone) +{ + unsigned char buffer[2000]; + + ringtone->NokiaBinary.Length=fread(buffer, 1, 500, file); + + if (buffer[18]==0x00 && buffer[21]!=0x02) { + /* DCT3, Unicode subformat, 62xx & 7110 */ + CopyUnicodeString(ringtone->Name,buffer+18); + ringtone->NokiaBinary.Length = ringtone->NokiaBinary.Length - (21+UnicodeLength(ringtone->Name)*2); + memcpy(ringtone->NokiaBinary.Frame,buffer+21+UnicodeLength(ringtone->Name)*2,ringtone->NokiaBinary.Length); + } else { + /* DCT3, normal subformat, 32xx/33xx/51xx/5210/5510/61xx/8xxx */ + EncodeUnicode(ringtone->Name,buffer+17,buffer[16]); + ringtone->NokiaBinary.Length = ringtone->NokiaBinary.Length - (19+UnicodeLength(ringtone->Name)); + memcpy(ringtone->NokiaBinary.Frame,buffer+19+UnicodeLength(ringtone->Name),ringtone->NokiaBinary.Length); + } + dbgprintf("Name \"%s\"\n",DecodeUnicodeString(ringtone->Name)); + return ERR_NONE; +} + +GSM_Error GSM_ReadRingtoneFile(char *FileName, GSM_Ringtone *ringtone) +{ + FILE *file; + unsigned char buffer[300]; + GSM_Error error = ERR_UNKNOWN; + + dbgprintf("Loading ringtone %s\n",FileName); + file = fopen(FileName, "rb"); + if (file == NULL) return ERR_CANTOPENFILE; + + /* Read the header of the file. */ + fread(buffer, 1, 4, file); + if (ringtone->Format == 0x00) { + ringtone->Format = RING_NOTETONE; + if (buffer[0]==0x00 && buffer[1]==0x00 && + buffer[2]==0x0C && buffer[3]==0x01) { + ringtone->Format = RING_NOKIABINARY; + } + if (buffer[0]==0x00 && buffer[1]==0x00 && + buffer[2]==0x00) { + ringtone->Format = RING_NOKIABINARY; + } + if (buffer[0]==0x4D && buffer[1]==0x54 && + buffer[2]==0x68 && buffer[3]==0x64) { + ringtone->Format = RING_MIDI; + } + } + rewind(file); + switch (ringtone->Format) { + case RING_NOTETONE: + if (buffer[0]==0x02 && buffer[1]==0x4A) { + error=loadott(file,ringtone); + } else if (buffer[0]==0xC7 && buffer[1]==0x45) { + error=loadcommunicator(file,ringtone); + } else { + error=loadrttl(file,ringtone); + } + ringtone->NoteTone.AllNotesScale=false; + break; + case RING_NOKIABINARY: + if (buffer[0]==0x00 && buffer[1]==0x00 && + buffer[2]==0x0C && buffer[3]==0x01) { + error=loadbin(file,ringtone); + } + if (buffer[0]==0x00 && buffer[1]==0x00 && + buffer[2]==0x00) { + error=loadre(file,ringtone); + } + break; + case RING_MIDI: + EncodeUnicode(ringtone->Name,FileName,strlen(FileName)); + error = loadpuremidi(file,ringtone); + } + fclose(file); + return(error); +} + +/* -------------------------- required with Nokia & RTTL ------------------- */ + +/* Beats per Minute like written in Smart Messaging */ +static int SM_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 GSM_RTTLGetTempo(int Beats) +{ + int i=0; + + while (Beats > SM_BeatsPerMinute[i] && SM_BeatsPerMinute[i] != 900) i++; + + return i<<3; +} + +/* This function packs the ringtone from the structure "ringtone" to + "package", where maxlength means length of package. + Function returns number of packed notes and change maxlength to + number of used chars in "package" */ +unsigned char GSM_EncodeNokiaRTTLRingtone(GSM_Ringtone ringtone, unsigned char *package, int *maxlength) +{ + unsigned char CommandLength = 0x02; + unsigned char Loop = 0x15; /* Infinite */ + + unsigned char Buffer[200]; + int StartBit=0, OldStartBit; + int StartBitHowManyCommands; + int HowManyCommands = 0; /* How many instructions packed */ + int HowManyNotes = 0; + int i,j; + bool started; + GSM_RingNote *Note; + + GSM_RingNoteScale DefScale = 255; + GSM_RingNoteStyle DefStyle = 255; + int DefTempo = 255; + + AddBufferByte(package, &StartBit, CommandLength, 8); + AddBufferByte(package, &StartBit, SM_Command_RingingToneProgramming, 7); + + /* According to specification we need have next part octet-aligned */ + BufferAlign(package, &StartBit); + + AddBufferByte(package, &StartBit, SM_Command_Sound, 7); + AddBufferByte(package, &StartBit, SM_Song_BasicSongType, 3); + + /* Packing the name of the tune. */ + EncodeUnicodeSpecialNOKIAChars(Buffer, ringtone.Name, UnicodeLength(ringtone.Name)); + AddBufferByte(package, &StartBit, ((unsigned char)(UnicodeLength(Buffer)<<4)), 4); + AddBuffer(package, &StartBit, DecodeUnicodeString(Buffer), 8*UnicodeLength(Buffer)); + + /* Packing info about song pattern */ + AddBufferByte(package, &StartBit, 0x01, 8); //one pattern + AddBufferByte(package, &StartBit, SM_InstructionID_PatternHeaderId, 3); + AddBufferByte(package, &StartBit, SM_PatternID_A_part, 2); + AddBufferByte(package, &StartBit, ((unsigned char)(Loop<<4)), 4); + + /* Later here will be HowManyCommands */ + StartBitHowManyCommands=StartBit; + StartBit = StartBit + 8; + + started = false; + for (i=0; i<ringtone.NoteTone.NrCommands; i++) { + if (ringtone.NoteTone.Commands[i].Type != RING_Note) { + HowManyNotes++; + continue; + } + Note = &ringtone.NoteTone.Commands[i].Note; + if (!started) { + /* First note can't be Pause - it makes problems + * for example with PC Composer + */ + if (Note->Note != Note_Pause) started = true; + } + if (!started) { + HowManyNotes++; + continue; + } + OldStartBit = StartBit; + /* we don't write Scale & Style info before "Pause" note - it saves place */ + if (Note->Note!=Note_Pause) { + if (DefScale != Note->Scale || ringtone.NoteTone.AllNotesScale) { + j = StartBit+5+8; + BufferAlignNumber(&j); + if ((j/8)>(*maxlength)) { + StartBit = OldStartBit; + break; + } + DefScale = Note->Scale; + AddBufferByte(package, &StartBit, SM_InstructionID_ScaleInstructionId, 3); + AddBufferByte(package, &StartBit, ((unsigned char)((DefScale-4)<<6)), 2); + HowManyCommands++; + } + if (DefStyle != Note->Style) { + j = StartBit+5+8; + BufferAlignNumber(&j); + if ((j/8)>(*maxlength)) { + StartBit = OldStartBit; + break; + } + DefStyle = Note->Style; + AddBufferByte(package, &StartBit, SM_InstructionID_StyleInstructionId, 3); + AddBufferByte(package, &StartBit, ((unsigned char)DefStyle), 2); + HowManyCommands++; + } + } + /* Beats per minute/tempo of the tune */ + if (DefTempo != GSM_RTTLGetTempo(Note->Tempo)) { + j = StartBit+8+8; + BufferAlignNumber(&j); + if ((j/8)>(*maxlength)) { + StartBit = OldStartBit; + break; + } + DefTempo=GSM_RTTLGetTempo(Note->Tempo); + /* Adding beats per minute (tempo) of the tune */ + AddBufferByte(package, &StartBit, SM_InstructionID_TempoInstructionId, 3); + AddBufferByte(package, &StartBit, ((unsigned char)DefTempo), 5); + HowManyCommands++; + } + j = StartBit+12+8; + BufferAlignNumber(&j); + if ((j/8)>(*maxlength)) { + StartBit = OldStartBit; + break; + } + /* Note */ + AddBufferByte(package, &StartBit, SM_InstructionID_NoteInstructionId, 3); + AddBufferByte(package, &StartBit, ((unsigned char)Note->Note), 4); + AddBufferByte(package, &StartBit, ((unsigned char)Note->Duration), 3); + AddBufferByte(package, &StartBit, ((unsigned char)Note->DurationSpec), 2); + HowManyCommands++; + /* We are sure, we pack it for SMS or setting to phone, not for OTT file */ + if (*maxlength<1000) { + /* Like Pc Composer say - before of phone limitations...*/ + if (HowManyNotes==130-1) break; + } + HowManyNotes++; + } + + BufferAlign(package, &StartBit); + AddBufferByte(package, &StartBit, SM_CommandEnd_CommandEnd, 8); + + OldStartBit = StartBit; + StartBit = StartBitHowManyCommands; + /* HowManyCommands */ + AddBufferByte(package, &StartBit, ((unsigned char)HowManyCommands), 8); + StartBit = OldStartBit; + + *maxlength=StartBit/8; + + return(i); +} + +GSM_Error GSM_DecodeNokiaRTTLRingtone(GSM_Ringtone *ringtone, unsigned char *package, int maxlength) +{ + int StartBit=0, HowMany, l, q, i, spec; + char Buffer[100]; + GSM_RingNote *Note; + + /* Default ringtone parameters */ + GSM_RingNoteScale DefScale = Scale_880; + GSM_RingNoteStyle DefStyle = NaturalStyle; + int DefTempo = 63; + + ringtone->Format = RING_NOTETONE; + ringtone->NoteTone.NrCommands = 0; + + GetBufferInt(package,&StartBit,&l,8); + if (l!=0x02) { + dbgprintf("Not header\n"); + return ERR_NOTSUPPORTED; + } + + GetBufferInt(package,&StartBit,&l,7); + if (l!=SM_Command_RingingToneProgramming) { + dbgprintf("Not RingingToneProgramming\n"); + return ERR_NOTSUPPORTED; + } + + /* According to specification we need have next part octet-aligned */ + BufferAlignNumber(&StartBit); + + GetBufferInt(package,&StartBit,&l,7); + if (l!=SM_Command_Sound) { + dbgprintf("Not Sound\n"); + return ERR_NOTSUPPORTED; + } + + GetBufferInt(package,&StartBit,&l,3); + if (l!=SM_Song_BasicSongType) { + dbgprintf("Not BasicSongType\n"); + return ERR_NOTSUPPORTED; + } + + /* Getting length of the tune name */ + GetBufferInt(package,&StartBit,&l,4); + l=l>>4; + + /* Unpacking the name of the tune. */ + GetBuffer(package, &StartBit, Buffer, 8*l); + Buffer[l]=0; + EncodeUnicode(ringtone->Name,Buffer,strlen(Buffer)); + DecodeUnicodeSpecialNOKIAChars(Buffer, ringtone->Name, UnicodeLength(ringtone->Name)); + CopyUnicodeString(ringtone->Name,Buffer); + + GetBufferInt(package,&StartBit,&l,8); + dbgprintf("Number of song patterns: %i\n",l); + /* we support only one song pattern */ + if (l!=1) return ERR_NOTSUPPORTED; + + GetBufferInt(package,&StartBit,&l,3); + if (l!=SM_InstructionID_PatternHeaderId) { + dbgprintf("Not PatternHeaderId\n"); + return ERR_NOTSUPPORTED; + } + + /* Pattern ID - we ignore it */ + StartBit+=2; + + GetBufferInt(package,&StartBit,&l,4); + l=l>>4; + dbgprintf("Loop value: %i\n",l); + + HowMany=0; + GetBufferInt(package, &StartBit, &HowMany, 8); + + for (i=0;i<HowMany;i++) { + GetBufferInt(package,&StartBit,&q,3); + switch (q) { + case SM_InstructionID_VolumeInstructionId: + StartBit+=4; + break; + case SM_InstructionID_StyleInstructionId: + GetBufferInt(package,&StartBit,&l,2); + if (l>=NaturalStyle && l<=StaccatoStyle) DefStyle = l; + break; + case SM_InstructionID_TempoInstructionId: + GetBufferInt(package,&StartBit,&l,5); + DefTempo=SM_BeatsPerMinute[l>>3]; + break; + case SM_InstructionID_ScaleInstructionId: + GetBufferInt(package,&StartBit,&l,2); + DefScale=(l>>6)+4; + break; + case SM_InstructionID_NoteInstructionId: + Note = &ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Note; + ringtone->NoteTone.Commands[ringtone->NoteTone.NrCommands].Type = RING_Note; + + GetBufferInt(package,&StartBit,&l,4); + Note->Note=Note_Pause; + if (l >= Note_C && l <= Note_H) Note->Note = l; + + GetBufferInt(package,&StartBit,&l,3); + if (l >= Duration_Full && l <= Duration_1_32) Note->Duration = l; + + GetBufferInt(package,&StartBit,&spec,2); + if (spec >= NoSpecialDuration && spec <= Length_2_3) { + Note->DurationSpec = spec; + } + + Note->Scale = DefScale; + Note->Style = DefStyle; + Note->Tempo = DefTempo; + if (ringtone->NoteTone.NrCommands==MAX_RINGTONE_NOTES) break; + ringtone->NoteTone.NrCommands++; + break; + default: + dbgprintf("Unsupported block %i %i\n",q,i); + return ERR_NOTSUPPORTED; + } + } + return ERR_NONE; +} + +static void RTTL2Binary(GSM_Ringtone *dest, GSM_Ringtone *src) +{ + int current = 0, i, note, lastnote = 0, duration; + GSM_RingNote *Note; + unsigned char end[] = {0x40, 0x7D, 0x40, 0x5C, 0x0A, 0xFE, 0x40, + 0x20, 0x40, 0x7D, 0x40, 0x37, 0x0A, 0xFE, + 0x0A, 0x0A, 0x40, 0x32, 0x07, 0x0B}; + + strcpy(dest->NokiaBinary.Frame+current,"\x02\xFC\x09"); current=current+3; + dest->NokiaBinary.Frame[current++]=0x00; + +/* This command can be used to loop, where 0xLL = 0x01 - 0x10 + * 0x01=loop once [...] 0x10=loop infinite + * Commented now + + dest->NokiaBinary.Frame[current++]=0x05; + dest->NokiaBinary.Frame[current++]=0xLL; + */ + strcpy(dest->NokiaBinary.Frame+current,"\x0A\x01"); current=current+2; + + for (i=0; i<src->NoteTone.NrCommands; i++) { + if (src->NoteTone.Commands[i].Type != RING_Note) continue; + + Note = &src->NoteTone.Commands[i].Note; + note = 64; /* Pause */ + if (Note->Note!=Note_Pause) { + if (Note->Note >= Note_C && Note->Note <= Note_H) { + note = 113 + Note->Note/16; + } + switch (Note->Scale) { + case Scale_440 : break; + case Scale_880 : note = note + 12; break; + case Scale_1760: note = note + 24; break; + case Scale_3520: note = note + 36; break; + default : break; + } + } + + /* In 7110 we have 8 ms long sounds */ + duration = 60000 * GSM_RingNoteGetFullDuration(*Note) / Note->Tempo / 256; + + switch (Note->Style) { + case StaccatoStyle: + if (duration) { + /* Note needs only one sound */ + dest->NokiaBinary.Frame[current++] = note; + dest->NokiaBinary.Frame[current++] = 1; + duration--; + } + note = 0x40; /* The rest is pause */ + case NaturalStyle: + if (note != 0x40 && duration) { + dest->NokiaBinary.Frame[current++] = 0x40; + /* There is small pause between notes */ + dest->NokiaBinary.Frame[current++] = 1; + duration--; + } + default: + if (note != 0x40 && note == lastnote && duration) { + dest->NokiaBinary.Frame[current++] = 0x40; + /* There is small pause between same notes */ + dest->NokiaBinary.Frame[current++] = 1; + duration--; + } + while (duration > 125) { + dest->NokiaBinary.Frame[current++] = note; + dest->NokiaBinary.Frame[current++] = 125; + duration -= 125; + } + dest->NokiaBinary.Frame[current++] = note; + dest->NokiaBinary.Frame[current++] = duration; + } + lastnote = note; + } + for (i = 0; i < (int)sizeof(end); i++) dest->NokiaBinary.Frame[current++] = end[i]; + dest->NokiaBinary.Length=current; +} + +static void Binary2RTTL(GSM_Ringtone *dest, GSM_Ringtone *src) +{ + int i = 3, j, z, NrNotes = 0, repeat = 0, accuracy; + int StartRepeat = 0, EndRepeat, Speed; + unsigned char command,length=0; + int NotesLen[500]; + GSM_RingNoteScale NotesScale[500]; + GSM_RingNoteNote Notes[500]; + int Lengths[6*4]; + GSM_RingNoteDurationSpec DurationSpec[6*4]; + GSM_RingNoteDuration Duration[6*4]; + bool foundlen; + GSM_RingNote *Note; + + while (i<src->NokiaBinary.Length) { + command = src->NokiaBinary.Frame[i]; + i++; + if (command != 0x06 && command != 0x00 && command != 0x09) { + length = src->NokiaBinary.Frame[i]; + i++; + dbgprintf("Block %02x %02x - ",length,command); + } else dbgprintf("Block %02x - ",command); + if (command >= 114 && command <= 161) { + dbgprintf("note\n"); + if (command >= 114 && command <= 124) { + NotesScale[NrNotes] = Scale_440; command -= 114; + } else if (command >= 125 && command <= 137) { + NotesScale[NrNotes] = Scale_880; command -= 126; + } else if (command >= 138 && command <= 149) { + NotesScale[NrNotes] = Scale_1760; command -= 138; + } else if (command >= 150 && command <= 161) { + NotesScale[NrNotes] = Scale_3520; command -= 150; + } + switch (command) { + case 0 : Notes[NrNotes] = Note_C; break; + case 1 : Notes[NrNotes] = Note_Cis; break; + case 2 : Notes[NrNotes] = Note_D; break; + case 3 : Notes[NrNotes] = Note_Dis; break; + case 4 : Notes[NrNotes] = Note_E; break; + case 5 : Notes[NrNotes] = Note_F; break; + case 6 : Notes[NrNotes] = Note_Fis; break; + case 7 : Notes[NrNotes] = Note_G; break; + case 8 : Notes[NrNotes] = Note_Gis; break; + case 9 : Notes[NrNotes] = Note_A; break; + case 10 : Notes[NrNotes] = Note_Ais; break; + case 11 : Notes[NrNotes] = Note_H; break; + } + if (NrNotes > 0) { + if (Notes[NrNotes-1] == Notes[NrNotes] && + NotesScale[NrNotes-1] == NotesScale[NrNotes]) { + NotesLen[NrNotes-1]+=length; + } else { + NotesLen[NrNotes]=length; + NrNotes++; + } + } else { + NotesLen[NrNotes]=length; + NrNotes++; + } + } else switch (command) { + case 0x00: + dbgprintf("Unknown\n"); + break; + case 0x05: + dbgprintf("repeat %i times\n",length); + repeat = length; + StartRepeat = NrNotes; + break; + case 0x06: + dbgprintf("end repeat\n"); + EndRepeat = NrNotes; + for (z=0;z<repeat-1;z++) { + for (j=StartRepeat;j<EndRepeat;j++) { + Notes[NrNotes] = Notes[j]; + NotesScale[NrNotes] = NotesScale[j]; + NotesLen[NrNotes] = NotesLen[j]; + NrNotes++; + dbgprintf("Adding repeat note %i %i\n",Notes[j],NotesLen[j]); + } + } + break; + case 0x07: + if (length == 0x0B) { + dbgprintf("Ringtone end\n"); + i = src->NokiaBinary.Length + 1; + } + break; + case 0x09: + dbgprintf("Unknown\n"); + break; + case 0x0A: + if (length == 0x01) { + dbgprintf("Let's start our song\n"); + break; + } + if (length == 0x0A) { + dbgprintf("Ending joining note\n"); + break; + } + if (length == 0xFE) { + dbgprintf("Starting joining note\n"); + break; + } + break; + case 0x40: + dbgprintf("Pause\n"); + Notes[NrNotes] = Note_Pause; + if (NrNotes > 0) { + if (Notes[NrNotes-1] == Notes[NrNotes] && + NotesScale[NrNotes-1] == NotesScale[NrNotes]) { + NotesLen[NrNotes-1]+=length; + } else { + NotesLen[NrNotes]=length; + NrNotes++; + } + } else { + NotesLen[NrNotes]=length; + NrNotes++; + } + break; + default: + dbgprintf("Unknown\n"); + } + } + + while (NrNotes>0) { + if (Notes[NrNotes-1] == Note_Pause) { + NrNotes--; + } else break; + } + + for (accuracy=1; accuracy<5; accuracy++) { + i = 1; + while (i < 1000) { + Lengths[0] = 30000/i; + for (j=0;j<5;j++) Lengths[j+1] = Lengths[j] / 2; + for (j=0;j<6;j++) Lengths[6+j] = Lengths[j] * 3/2; + for (j=0;j<6;j++) Lengths[12+j] = Lengths[j] * 9/4; + for (j=0;j<6;j++) Lengths[18+j] = Lengths[j] * 2/3; + +#ifdef DEBUG + dbgprintf("Length matrix (%i) : ",i); + for (j=0;j<6*4;j++) dbgprintf("%i ",Lengths[j]); + dbgprintf("\n"); +#endif + foundlen = false; + + for (j=0;j<NrNotes;j++) { + dbgprintf("Comparing to %i\n",NotesLen[j]); + foundlen = false; + for (z=0;z<6*4;z++) { + if (NotesLen[j] - Lengths[z] > -accuracy && + NotesLen[j] - Lengths[z] < accuracy) { + foundlen = true; + break; + } + } + if (!foundlen) break; + } + if (foundlen) break; + i++; + } + + if (foundlen) { + Speed = i; + Duration[5] = Duration_1_32; Duration[4] = Duration_1_16; + Duration[3] = Duration_1_8; Duration[2] = Duration_1_4; + Duration[1] = Duration_1_2; Duration[0] = Duration_Full; + for (i=0;i<6;i++) Duration[i] = Duration[i]; + for (i=0;i<6;i++) Duration[i+6] = Duration[i]; + for (i=0;i<6;i++) Duration[i+12] = Duration[i]; + for (i=0;i<6;i++) Duration[i+18] = Duration[i]; + for (i=0;i<6;i++) DurationSpec[i] = NoSpecialDuration; + for (i=0;i<6;i++) DurationSpec[i+6] = DottedNote; + for (i=0;i<6;i++) DurationSpec[i+12] = DoubleDottedNote; + for (i=0;i<6;i++) DurationSpec[i+18] = Length_2_3; + + for (i=0;i<NrNotes;i++) { + dest->NoteTone.Commands[i].Type = RING_Note; + Note = &dest->NoteTone.Commands[i].Note; + Note->Note = Notes[i]; + Note->Tempo = Speed; + Note->Style = ContinuousStyle; + if (Notes[i] != Note_Pause) Note->Scale = NotesScale[i]; + for (z=0;z<6*4;z++) { + if (NotesLen[i] - Lengths[z] > -accuracy && + NotesLen[i] - Lengths[z] < accuracy) { + Note->Duration = Duration[z]; + Note->DurationSpec = DurationSpec[z]; + /* Trick from PPM Edit */ + if (Note->DurationSpec == DoubleDottedNote) { + switch (Note->Duration) { + case Duration_Full:Note->Duration = Duration_Full;break; + case Duration_1_2 :Note->Duration = Duration_Full;break; + case Duration_1_4 :Note->Duration = Duration_1_2; break; + case Duration_1_8 :Note->Duration = Duration_1_4; break; + case Duration_1_16:Note->Duration = Duration_1_8; break; + case Duration_1_32:Note->Duration = Duration_1_16;break; + } + Note->DurationSpec = NoSpecialDuration; + } + /* Here happy creation */ + if (Note->DurationSpec == Length_2_3) { + Note->DurationSpec = NoSpecialDuration; + } + + break; + } + } + } + dest->NoteTone.NrCommands = NrNotes; + dbgprintf("speed = %i\n",Speed); + break; + } + } + + if (!foundlen) dest->NoteTone.NrCommands = 0; +} + +GSM_Error GSM_RingtoneConvert(GSM_Ringtone *dest, GSM_Ringtone *src, GSM_RingtoneFormat Format) +{ + dest->Format = Format; + CopyUnicodeString(dest->Name,src->Name); + if (src->Format==RING_NOTETONE && Format==RING_NOKIABINARY) { + RTTL2Binary(dest, src); + return ERR_NONE; + } + if (src->Format==RING_NOKIABINARY && Format==RING_NOTETONE) { + Binary2RTTL(dest, src); + return ERR_NONE; + } + /* The same source and target format */ + if (src->Format==Format) { + memcpy(dest,src,sizeof(GSM_Ringtone)); + return ERR_NONE; + } + return ERR_NOTIMPLEMENTED; +} + +/* 0 = No header and footer, 0.5 = partial header and footer, + * 1.0 = IMelody 1.0, 1.2 = IMelody 1.2 */ +unsigned char GSM_EncodeEMSSound(GSM_Ringtone ringtone, unsigned char *package, int *maxlength, double version, bool start) +{ + int i, NrNotes = 0, Len, Max = *maxlength; + + GSM_RingNote *Note; + + GSM_RingNoteScale DefNoteScale; + GSM_RingNoteStyle DefNoteStyle=0; + int DefNoteTempo=0; + + bool started = false, end; + + *maxlength = 0; + + if (start) { + if (version != 0) *maxlength+=sprintf(package,"BEGIN:IMELODY%c%c",13,10); + if (version == 1.0) *maxlength+=sprintf(package+(*maxlength),"VERSION:1.0%c%c",13,10); + if (version == 1.2) *maxlength+=sprintf(package+(*maxlength),"VERSION:1.2%c%c",13,10); + if (version >= 1.0) *maxlength+=sprintf(package+(*maxlength),"FORMAT:CLASS1.0%c%c",13,10); + if (version == 1.2) *maxlength+=sprintf(package+(*maxlength),"NAME:%s%c%c",DecodeUnicodeString(ringtone.Name),13,10); + } + + DefNoteScale = Scale_880; /* by iMelody definition */ + + for (i=0;i<ringtone.NoteTone.NrCommands;i++) { + Len = *maxlength; + if (ringtone.NoteTone.Commands[i].Type != RING_Note) continue; + + Note = &ringtone.NoteTone.Commands[i].Note; + if (Note->Note == Note_Pause) continue; + + if (version == 1.2 && start) { + /* Save the default tempo */ + DefNoteTempo = Note->Tempo; + Len+=sprintf(package+Len,"BEAT:%i%c%c",DefNoteTempo,13,10); + dbgprintf("DefNoteTempo=%d\n",DefNoteTempo); + + /* Save default style */ + DefNoteStyle = Note->Style; + switch (DefNoteStyle) { + case NaturalStyle :Len+=sprintf(package+Len,"STYLE:S0%c%c",13,10); break; + case ContinuousStyle:Len+=sprintf(package+Len,"STYLE:S1%c%c",13,10); break; + case StaccatoStyle :Len+=sprintf(package+Len,"STYLE:S2%c%c",13,10); break; + } + } + Len+=sprintf(package+Len,"MELODY:"); + if (version != 0) { + /* 15 = Len of END:IMELODY... */ + if ((Len+15) > Max) { end = true; break; } + } else { + if (Len > Max) { end = true; break; } + } + *maxlength = Len; + break; + } + + for (i=0;i<ringtone.NoteTone.NrCommands;i++) { + end = false; + Len = *maxlength; + switch (ringtone.NoteTone.Commands[i].Type) { + case RING_Note: + Note = &ringtone.NoteTone.Commands[i].Note; + if (!started && Note->Note != Note_Pause) started = true; + if (!started) break; + if (Note->Note!=Note_Pause && Note->Scale != DefNoteScale) { + Len+=sprintf(package+Len,"*%i",Note->Scale-1); + } + switch (Note->Note) { + case Note_C :Len+=sprintf(package+Len,"c"); break; + case Note_Cis :Len+=sprintf(package+Len,"#c");break; + case Note_D :Len+=sprintf(package+Len,"d"); break; + case Note_Dis :Len+=sprintf(package+Len,"#d");break; + case Note_E :Len+=sprintf(package+Len,"e"); break; + case Note_F :Len+=sprintf(package+Len,"f"); break; + case Note_Fis :Len+=sprintf(package+Len,"#f");break; + case Note_G :Len+=sprintf(package+Len,"g"); break; + case Note_Gis :Len+=sprintf(package+Len,"#g");break; + case Note_A :Len+=sprintf(package+Len,"a"); break; + case Note_Ais :Len+=sprintf(package+Len,"#a");break; + case Note_H :Len+=sprintf(package+Len,"b"); break; + case Note_Pause :Len+=sprintf(package+Len,"r"); break; + } + switch (Note->Duration) { + case Duration_Full : package[Len++]='0'; break; + case Duration_1_2 : package[Len++]='1'; break; + case Duration_1_4 : package[Len++]='2'; break; + case Duration_1_8 : package[Len++]='3'; break; + case Duration_1_16 : package[Len++]='4'; break; + case Duration_1_32 : package[Len++]='5'; break; + default : break; + } + switch (Note->DurationSpec) { + case DottedNote : package[Len++] = '.'; break; + case DoubleDottedNote : package[Len++] = ':'; break; + case Length_2_3 : package[Len++] = ';'; break; + default : break; + } + if (version != 0) { + /* 15 = Len of END:IMELODY... */ + if ((Len+15) > Max) { end = true; break; } + } else { + if (Len > Max) { end = true; break; } + } + *maxlength = Len; + break; + case RING_DisableLED: + if ((Len + 6) > Max) { end = true; break; } + (*maxlength)+=sprintf(package+Len,"ledoff"); + break; + case RING_EnableLED: + if ((Len + 5) > Max) { end = true; break; } + (*maxlength)+=sprintf(package+Len,"ledon"); + break; + case RING_DisableVibra: + if ((Len + 7) > Max) { end = true; break; } + (*maxlength)+=sprintf(package+Len,"vibeoff"); + break; + case RING_EnableVibra: + if ((Len + 6) > Max) { end = true; break; } + (*maxlength)+=sprintf(package+Len,"vibeon"); + break; + case RING_DisableLight: + if ((Len + 7) > Max) { end = true; break; } + (*maxlength)+=sprintf(package+Len,"backoff"); + break; + case RING_EnableLight: + if ((Len + 6) > Max) { end = true; break; } + (*maxlength)+=sprintf(package+Len,"backon"); + break; + default: + break; + } + if (end) break; + NrNotes ++; + } + + if (version != 0) *maxlength+=sprintf(package+(*maxlength),"%c%cEND:IMELODY%c%c",13,10,13,10); + + return NrNotes; +} + +char *GSM_GetRingtoneName(GSM_AllRingtonesInfo *Info, int ID) +{ + int i; + static char ala[2]; + + for (i=0;i<Info->Number;i++) { + if (Info->Ringtone[i].ID == ID) return Info->Ringtone[i].Name; + } + + ala[0] = 0; + ala[1] = 0; + return ala; +} + +/* How should editor hadle tabs in this file? Add editor commands here. + * vim: noexpandtab sw=8 ts=8 sts=8: + */ |