summaryrefslogtreecommitdiffabout
path: root/gammu/emb/common/service/gsmring.c
Side-by-side diff
Diffstat (limited to 'gammu/emb/common/service/gsmring.c') (more/less context) (show whitespace changes)
-rw-r--r--gammu/emb/common/service/gsmring.c45
1 files changed, 43 insertions, 2 deletions
diff --git a/gammu/emb/common/service/gsmring.c b/gammu/emb/common/service/gsmring.c
index 7df46f1..dab028c 100644
--- a/gammu/emb/common/service/gsmring.c
+++ b/gammu/emb/common/service/gsmring.c
@@ -1,57 +1,60 @@
/* (c) 2001-2004 by Marcin Wiacek */
-/* Based on some work from Ralf Thelen (7110 ringtones),
- * Gnokii (RTTL and SM) and others
+/* Based on some work from Ralf Thelen (7110 ringtones) and others */
+/* Based on some work (RTTL and SM) from Gnokii (www.gnokii.org)
+ * (C) 1999-2000 Hugh Blemings & Pavel Janik ml. (C) 2001-2004 Pawel Kot
+ * GNU GPL version 2 or later
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
+#include <sys/stat.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) {
@@ -111,96 +114,102 @@ GSM_Error savewav(FILE *file, GSM_Ringtone *ringtone)
DATA_Buffer[j] = ((int)(0.5*50000));
#endif
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;
}
+static GSM_Error savemmf(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;
@@ -440,96 +449,99 @@ void savemid(FILE* file, GSM_Ringtone *ringtone)
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;
+ case RING_MMF:
+ savemmf(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;
@@ -724,169 +736,198 @@ 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 loadmmf(FILE *file, GSM_Ringtone *ringtone)
+{
+ struct stat st;
+ char *buffer;
+ int length;
+
+ dbgprintf("loading smaf file\n");
+ fstat(fileno(file), &st);
+ ringtone->BinaryTone.Length = length = st.st_size;
+ ringtone->BinaryTone.Buffer = buffer = malloc(length);
+ if (buffer == NULL)
+ return ERR_MOREMEMORY;
+ fread(buffer, 1, length, file);
+
+ dbgprintf("Length %i name \"%s\"\n", 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;
}
+ if (buffer[0]==0x4D && buffer[1]==0x4D &&
+ buffer[2]==0x4D && buffer[3]==0x44) {
+ ringtone->Format = RING_MMF;
+ }
}
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);
+ break;
+ case RING_MMF:
+ EncodeUnicode(ringtone->Name,FileName,strlen(FileName));
+ error = loadmmf(file,ringtone);
+ break;
}
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);