© Иван Рощин, Москва
| ZXNet | : 500:95/462.53 |
| : bestview@mtu-net.ru | |
| WWW | : http://www.ivr.da.ru |
(«Радиомир. Ваш компьютер» 7/2005)
Иногда бывает нужно изменить порядок каналов в музыкальном модуле, написанном в Impulse Tracker, например, чтобы каналы с одним инструментом располагались рядом для облегчения последующего редактирования. Но в самом Impulse Tracker нет удобного способа это сделать. Можно, конечно, добиться нужного результата с помощью копирования блоков, но это большой объём ручной работы.
Я пробовал найти в Интернете готовую программу для изменения порядка
каналов в
/* =============================
File: "reord_it.c"
Compiler: Turbo C 2.0
============================= */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define byte unsigned char /* 1 байт. */
#define word unsigned int /* 2 байта. */
/* ==================== Описание функций ==================== */
int get_new_order (char* s);
int read_2 (char** s, int* n1, int* n2);
FILE* fopen_1 (char* name, char* mode);
void fclose_1 (FILE* f);
int fgetc_1 (FILE* f);
void fputc_1 (int c, FILE* f);
void fseek_1 (FILE* f, long offset, int whence);
/* ======================= Переменные ======================= */
int new_order[64]; /* Для каждого канала здесь хранится
его новый номер. */
byte pan_vol_old[128]; /* Массивы со старыми и новыми */
byte pan_vol_new[128]; /* значениями начальной панорамы */
/* и громкости каналов. */
/* ================== Пошла сама программа ================== */
int main (int argc, char *argv[])
{
FILE *f_src;
int i,j;
printf ("\n\
+----------------------------------------------------------+\n\
|Program for reordering channels in Impulse Tracker modules|\n\
|(c) Ivan Roshin, Moscow, 18 Jul 2004 |\n\
|E-mail: bestview@mtu-net.ru WWW: http://www.ivr.da.ru|\n\
+----------------------------------------------------------+\n\
");
if (argc!=3)
{
printf ("\n\
Syntax: reord_it.exe NewChannel=OldChannel,... it-file\n\n\
Example: for swap channels 2 and 5, and swap channels 1 and 3\n\
in it-file 'demo.it', type:\n\n\
reord_it.exe 2=5,5=2,1=3,3=1 demo.it\n\n");
exit(1);
}
/* Обрабатываем указанный в командной строке аргумент,
определяющий новый порядок расположения каналов. */
if (get_new_order(argv[1]))
{printf ("\nIncorrect argument!\n\n"); exit (1);}
/* Открываем исходный файл. */
f_src=fopen_1(argv[2],"rb+");
printf ("File: %s ",argv[2]);
/* =================== Создание bak-файла =================== */
{
FILE *f_bak;
char name_bak[13];
/* Формируем имя bak-файла из имени исходного файла. */
i=0;
while (!((argv[2][i]=='.')||(argv[2][i]==0)))
{
name_bak[i]=argv[2][i];
i++;
if (i>8) {printf ("\nIncorrect file name!\n\n"); exit (1);}
}
strcpy(name_bak+i,".bak");
/* Копируем содержимое исходного файла в bak-файл. */
f_bak=fopen_1(name_bak,"wb");
while ((i=fgetc_1(f_src))!=EOF) fputc_1 (i,f_bak);
fclose_1(f_bak);
}
/* =================== Обработка it-файла =================== */
/* Проверка сигнатуры: первые 4 байта - "IMPM". */
{
byte signature[4];
fseek_1 (f_src,0L,SEEK_SET);
if (fread(&signature,(size_t)4,1,f_src)!=1)
{printf ("\nRead error!\n\n"); exit (1);}
if (strncmp(signature,"IMPM",(size_t)4)!=0)
{printf ("\nIncorrect file format!\n\n"); exit (1);}
}
/* Читаем начальные значения панорамы и громкости каналов,
меняем их порядок в соответствии с массивом new_order
и записываем обратно. */
fseek_1 (f_src,0x40L,SEEK_SET);
if (fread(&pan_vol_old,(size_t)128,1,f_src)!=1)
{printf ("\nRead error!\n\n"); exit (1);}
for (i=0;i<64;i++)
{
pan_vol_new[new_order[i]-1]=pan_vol_old[i]; /* Panning */
pan_vol_new[new_order[i]-1+64]=pan_vol_old[i+64]; /* Volume */
}
fseek_1(f_src,0x40L,SEEK_SET);
if (fwrite(&pan_vol_new,(size_t)128,1,f_src)!=1)
{printf ("\nWrite error!\n\n"); exit (1);}
/* Обработка паттернов. */
{
word OrdNum; /* Кол-во позиций. */
word InsNum; /* Кол-во инструментов. */
word SmpNum; /* Кол-во сэмплов. */
word PatNum; /* Кол-во паттернов. */
long current_p; /* Смещение до текущего паттерна. */
word rows; /* Кол-во строк в текущем паттерне. */
fseek_1(f_src,0x20L,SEEK_SET);
OrdNum=fgetc_1(f_src); OrdNum|=fgetc_1(f_src)<<8;
InsNum=fgetc_1(f_src); InsNum|=fgetc_1(f_src)<<8;
SmpNum=fgetc_1(f_src); SmpNum|=fgetc_1(f_src)<<8;
PatNum=fgetc_1(f_src); PatNum|=fgetc_1(f_src)<<8;
for (i=0;i<PatNum;i++)
{
fseek_1(f_src,0xC0L+OrdNum+InsNum*4L+SmpNum*4L+i*4L,SEEK_SET);
/* Читаем смещение до текущего паттерна. */
current_p=fgetc_1(f_src);
current_p|=(long)fgetc_1(f_src)<<8;
current_p|=(long)fgetc_1(f_src)<<16;
current_p|=(long)fgetc_1(f_src)<<24;
/* Если смещение равно нулю - значит, пустой паттерн. */
if (current_p==0) continue;
/* Позиционируемся на начало паттерна + 2 байта, читаем
кол-во строк в паттерне и пропускаем 4 байта. */
fseek_1(f_src,current_p+2,SEEK_SET);
rows=fgetc_1(f_src); rows|=fgetc_1(f_src)<<8;
fseek_1(f_src,4L,SEEK_CUR);
/* Обработка строк паттерна. */
for (j=0;j<rows;j++)
{
byte old_mask_var[64];
byte ch_var;
byte channel;
byte mask_var;
/* Обработка одной строки. */
while ((ch_var=fgetc_1(f_src))!=0)
{
/* В младших битах прочитанного байта - номер канала.
Изменяем его по таблице new_order и записываем обратно,
сохраняя при этом значение старшего бита. */
channel=(ch_var-1)&63;
ch_var=new_order[channel]|(ch_var&128);
fseek_1(f_src,-1L,SEEK_CUR);
fputc_1 (ch_var,f_src);
fseek_1(f_src,0L,SEEK_CUR); /* Почему-то без этого
не работает. :( */
if (ch_var&128)
{
mask_var=fgetc_1(f_src);
old_mask_var[channel]=mask_var;
}
else mask_var=old_mask_var[channel];
/* Пропускаем неизменяемые байты строки паттерна. */
if (mask_var&1) fgetc_1 (f_src);
if (mask_var&2) fgetc_1 (f_src);
if (mask_var&4) fgetc_1 (f_src);
if (mask_var&8) {fgetc_1 (f_src); fgetc_1 (f_src);}
}
}
}
}
fclose_1 (f_src);
printf ("OK!\n\n");
exit (0);
}
/* =============================================================
Функция get_new_order - обработка указанного в командной
строке аргумента, определяющего новый порядок расположения
каналов (т.е. анализ строки вида "7=2,2=15,14=3,...",
построение на основе этих данных массива new_order, где для
каждого канала хранится его новый номер, и проверка
корректности полученного массива).
Вход: s - указатель на обрабатываемый аргумент.
Выход: 0 - OK, 1 - ошибка.
============================================================ */
int get_new_order (char* s)
{
int n1,n2;
int i,j;
for (i=0;i<64;i++) new_order[i]=i+1;
while (*s!=0) /* Пока строка не кончилась. */
{
if (read_2(&s,&n1,&n2)) return 1;
new_order[n2-1]=n1;
}
/* Проверка корректности полученного массива new_order (то есть
проверка, не допустил ли пользователь ошибок при указании
нового порядка расположения каналов): в массиве должно
присутствовать каждое из чисел от 1 до 64 (то есть массив
должен состоять из всех чисел от 1 до 64 в любом порядке).
В самом деле, если в массиве нет какого-то из этих чисел,
то соответствующему каналу формируемого модуля не будет
соответствовать никакой канал исходного модуля, а это
недопустимо, ведь можно изменять только порядок каналов. */
for (i=1;i<=64;i++)
{
for (j=0;j<64;j++) {if (new_order[j]==i) goto m1;}
return 1;
m1:;
}
return 0;
}
/* =============================================================
Функция read_2 - чтение первой пары значений из строки вида
"7=2,2=15,14=3,...".
Вход: s - адрес указателя на строку (после успешного выхода
из функции этот указатель изменяется так, чтобы
указывать на следующую пару значений в строке - т.е.
на "2=15,14=3,..."); n1 и n2 - адреса, по которым надо
поместить первое и второе прочитанные значения (в
формате int).
Выход: 0 - OK, 1 - ошибка.
============================================================ */
int read_2 (char** s, int* n1, int* n2)
{
if (isdigit(**s)) {*n1=**s-'0'; (*s)++;} else return 1;
if (isdigit(**s)) {*n1=*n1*10+**s-'0'; (*s)++;}
if (**s!='=') return 1;
(*s)++;
if (isdigit(**s)) {*n2=**s-'0'; (*s)++;} else return 1;
if (isdigit(**s)) {*n2=*n2*10+**s-'0'; (*s)++;}
if (**s==',') {(*s)++; return 0;}
if (**s==0) return 0; else return 1;
}
/* =============================================================
Аналоги некоторых функций для работы с файлами (fopen,
fclose, fgetc, fputc, fseek). При ошибке прерывают выполнение
программы, печатая соответствующее сообщение; таким образом,
после вызова этих функций не надо проверять, была ли ошибка.
============================================================ */
FILE* fopen_1 (char* name, char* mode)
{
FILE* f;
char* s;
char* s1="read";
char* s2="write";
f=fopen(name,mode);
if (f==NULL)
{
if (mode[0]=='r') s=s1; else s=s2;
printf ("\nCan't open file \"%s\" for %s!\n\n",name,s);
exit (1);
}
return (f);
}
void fclose_1 (FILE* f)
{
if (fclose(f)==EOF) {printf ("\nClose error!\n\n"); exit (1);}
}
int fgetc_1 (FILE* f)
{
int c;
c=fgetc(f);
if ferror(f) {printf ("\nRead error!\n\n"); exit (1);}
return (c);
}
void fputc_1 (int c, FILE* f)
{
if (fputc(c,f)==EOF) {printf ("\nWrite error!\n\n"); exit (1);}
}
void fseek_1 (FILE* f, long offset, int whence)
{
if (fseek(f,offset,whence)!=0)
{printf ("\nPosition error!\n\n"); exit (1);}
}
Формат командной строки:
reord_it.exe информация_об_изменении_порядка_каналов it-file
Если запустить программу без аргументов, она выдаст краткую справку о работе с ней.
Информация об изменении порядка каналов представляет собой разделённые запятой пары чисел вида NewChannel=OldChannel, где NewChannel номер канала, куда будет перенесена информация, а OldChannel номер канала, откуда будет перенесена эта информация. То есть указывается соответствие каналов в исходном и получаемом модулях. Например, надо поменять местами содержимое 1 и 2 каналов, тогда в командной строке указываем «1=2,2=1» (без кавычек, без пробела после запятой): в первый канал переносится информация из второго, а во второй из первого. Можно записать и наоборот: «2=1,1=2» результат будет тем же самым.
Ещё пример: надо, чтобы в первом канале оказалось то, что раньше было во втором, во втором то, что раньше было в третьем, а в третьем то, что раньше было в первом. В командной строке указываем «1=2,2=3,3=1».
Обратите внимание: когда информация перемещается в некоторый канал X
из канала Y, то информация, которая была раньше в канале X,
также должна быть перемещена в
Обрабатываемый
Программа проверяет, чтобы обрабатываемый файл начинался с символов
«IMPM»; если это не так, значит, это не
Программа возвращает операционной системе код завершения: 0 успешное завершение, 1 ошибка. Сообщение об ошибке также выводится на экран.
При изменении порядка каналов меняются не только данные в паттернах, но и начальные значения панорамы и громкости каналов.
|
Другие мои статьи о создании и/или обработке модулей Impulse Tracker:
|