PSPx форум

PSPx форум (https://www.pspx.ru/forum/index.php)
-   Программирование для PSP (https://www.pspx.ru/forum/forumdisplay.php?f=101)
-   -   Играем мр3, мр4(h264), wav(pcm), atrac, aac (https://www.pspx.ru/forum/showthread.php?t=47325)

l3VGV 01.07.2007 18:22

Играем мр3, мр4(h264), wav(pcm), atrac, aac
 
Итак, рабочий пример без внешних либ, тока средцтвами самой консольки.

Код:

#include <pspkernel.h>
#include <pspctrl.h>
#include <pspmpeg.h>
#include <pspdisplay.h>
#include <pspdebug.h>
#include <psppower.h>
#include <stdio.h>
#include <stdlib.h>
#include <pspkernel.h>
#include <pspctrl.h>
#include <psppower.h>
#include <pspdebug.h>
#include <psprtc.h>
#include <pspsdk.h>
#include <pspaudiocodec.h>
#include <pspaudio.h>
#include <string.h>
#include <malloc.h>

int SetupCallbacks();

PSP_MODULE_INFO("MP3 decodeTest", 0x1000, 1, 1);
PSP_MAIN_THREAD_ATTR(0);

__attribute__ ((constructor))
void loaderInit(){
  pspKernelSetKernelPC();
  pspSdkInstallNoDeviceCheckPatch();
  pspSdkInstallNoPlainModuleCheckPatch();
  pspSdkInstallKernelLoadModulePatch();
}

SceCtrlData input;

static int bitrates[] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };

unsigned long mp3_codec_buffer[65] __attribute__((aligned(64)));
short mp3_mix_buffer1[1152 * 2] __attribute__((aligned(64)));
short mp3_mix_buffer2[1152 * 2] __attribute__((aligned(64)));

SceUID mp3_handle;
u8* mp3_data_buffer;
u16 mp3_data_align;
u32 mp3_sample_per_frame;
u16 mp3_channel_mode;
u32 mp3_data_start;
u32 mp3_data_size;
u8 mp3_getEDRAM;
u32 mp3_channels;
u32 mp3_samplerate;
int donext, bc, eof;

SceCtrlData pad;


int decode_thread(SceSize args, void *argp)
{

    eof = 0;
    fprintf(stderr,"$>entering main loop\n");     
  while( !eof ) {
          if (!donext)
            {
            sceKernelDelayThread(1);
            continue;
            }
         
      int samplesdecoded;
     
      if (!bc)
          memset(mp3_mix_buffer1, 0, mp3_sample_per_frame*2*2);
      else
          memset(mp3_mix_buffer2, 0, mp3_sample_per_frame*2*2);
     
      unsigned char mp3_header_buf[4];
      if ( sceIoRead( mp3_handle, mp3_header_buf, 4 ) != 4 ) {
          fprintf(stderr,"$>mp3_header_buf < 4 exitin \n");
        eof = 1;
        continue;
      }
      int mp3_header = mp3_header_buf[0];
      mp3_header = (mp3_header<<8) | mp3_header_buf[1];
      mp3_header = (mp3_header<<8) | mp3_header_buf[2];
      mp3_header = (mp3_header<<8) | mp3_header_buf[3];
     
      int bitrate = (mp3_header & 0xf000) >> 12;
      int padding = (mp3_header & 0x200) >> 9;
     
      int frame_size = 144000*bitrates[bitrate]/mp3_samplerate + padding;
     
      if ( mp3_data_buffer )
        free(mp3_data_buffer);
      mp3_data_buffer = (u8*)memalign(64, frame_size);
     
      sceIoLseek32(mp3_handle, mp3_data_start, PSP_SEEK_SET); //seek back
     
      if ( sceIoRead( mp3_handle, mp3_data_buffer, frame_size ) != frame_size ) {
          fprintf(stderr,"$>sceIoRead frame_size < frame_size exitin \n");
        eof = 1;
        continue;
      }
     
      mp3_data_start += frame_size;
     
      mp3_codec_buffer[6] = (unsigned long)mp3_data_buffer;
      if (!bc)
          mp3_codec_buffer[8] = (unsigned long)mp3_mix_buffer1;
      else
          mp3_codec_buffer[8] = (unsigned long)mp3_mix_buffer2;
     
      mp3_codec_buffer[7] = mp3_codec_buffer[10] = frame_size;
      mp3_codec_buffer[9] = mp3_sample_per_frame * 4;
 
      int res = sceAudiocodecDecode(mp3_codec_buffer, 0x1002);
     
      if ( res < 0 ) {
          fprintf(stderr,"$>sceAudiocodecDecode < 0< (%d) exitin \n", res);
        eof = 1;
        continue;
      }
      samplesdecoded = mp3_sample_per_frame;
      donext-=1;
      if (bc)
        bc=0;
      else
        bc=1;
     
  }

}


int InputThread(SceSize args, void *argp)
{

    while(!eof)
    {   

        sceCtrlPeekBufferPositive(&pad, 1);
        if( pad.Buttons & PSP_CTRL_TRIANGLE )
        {
        eof=1;
        }
        sceKernelDelayThread(1);   
               
    }
   
return 0;
}



void SetupInput( void)
{
SceUID itid = sceKernelCreateThread("Input thread",
InputThread,
0x18, 256 * 1024, PSP_THREAD_ATTR_USER, 0);
if (itid >= 0)
    sceKernelStartThread(itid, 0, 0);
}




int main(void)
{

 SetupCallbacks();   
 eof=0; 
  int result = pspSdkLoadStartModule("flash0:/kd/me_for_vsh.prx", PSP_MEMORY_PARTITION_KERNEL);   
  fprintf(stderr,"$>me_for_vsh %d\n", result);
 
  result = pspSdkLoadStartModule("flash0:/kd/videocodec.prx", PSP_MEMORY_PARTITION_KERNEL);
  fprintf(stderr,"$>videocodec %d\n", result);
       
  result = pspSdkLoadStartModule("flash0:/kd/audiocodec.prx", PSP_MEMORY_PARTITION_KERNEL);
    fprintf(stderr,"$>audiocodec %d\n", result);
           
  result = pspSdkLoadStartModule("flash0:/kd/mpegbase.prx", PSP_MEMORY_PARTITION_KERNEL);
  fprintf(stderr,"$>mpegbase %d\n", result);
 
  result = pspSdkLoadStartModule("flash0:/kd/mpeg_vsh.prx", PSP_MEMORY_PARTITION_USER);
  fprintf(stderr,"$>mpeg_vsh %d\n", result);
     
       
  SetupInput();
  pspSdkFixupImports(result);
 
  sceMpegInit();
 
  mp3_handle = sceIoOpen("ms0:/test.mp3", PSP_O_RDONLY, 0777);
  if (  ! mp3_handle )
      goto wait;
  fprintf(stderr,"$>mp3_handle %d\n", mp3_handle);
 
  mp3_channels = 2;            //довольно некрасивое ограничение, ну ладно нам для примера потянет....
  mp3_samplerate = 44100; //таже фигня
  mp3_sample_per_frame = 1152;
 
  mp3_data_start = sceIoLseek32(mp3_handle, 0, PSP_SEEK_CUR);
 
  memset(mp3_codec_buffer, 0, sizeof(mp3_codec_buffer));
 
  if ( sceAudiocodeCheckNeedMem(mp3_codec_buffer, 0x1002) < 0 )
        {
        fprintf(stderr,"$>sceAudiocodeCheckNeedMem <0 going to wait \n");
            goto wait;
            }
  if ( sceAudiocodecGetEDRAM(mp3_codec_buffer, 0x1002) < 0 )
              {
            fprintf(stderr,"$>sceAudiocodecGetEDRAM <0 going to wait \n");
        goto wait;
        }
  mp3_getEDRAM = 1;
 
  if ( sceAudiocodecInit(mp3_codec_buffer, 0x1002) < 0 ) {
      fprintf(stderr,"$>sceAudiocodecInit <0 going to wait \n");
      goto wait;
  }
 
 
   
    int channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, 1152, PSP_AUDIO_FORMAT_STEREO);
    if (channel < 0)
      {
          fprintf(stderr,"Error reserving channel\n");
          goto wait;
      }   
   
    bc=0;
   
    donext=0;
    int decodth = sceKernelCreateThread("decode thread", decode_thread, 0x8, 0x10000, PSP_THREAD_ATTR_USER, NULL);
    if (decodth < 0)
      {
          fprintf(stderr, "failed create decode thread\n");
          goto wait;
      }       
   
    sceKernelStartThread(decodth, 0, 0);
    fprintf(stderr, "decode thread %d\n", decodth);
    donext=1;
   
    while (!eof){
   
    donext=1;   
    if (!bc)
    sceAudioOutputBlocking(channel, 0x8000, mp3_mix_buffer1);
    else
    sceAudioOutputBlocking(channel, 0x8000, mp3_mix_buffer2);
    }
   

wait:
 
  if ( mp3_handle ) {
      sceIoClose(mp3_handle);
  }
  if ( mp3_data_buffer) {
      free(mp3_data_buffer);
  }
  if ( mp3_getEDRAM ) {
      sceAudiocodecReleaseEDRAM(mp3_codec_buffer);
  }
 
  sceCtrlReadBufferPositive(&input, 1);
  while(!(input.Buttons & PSP_CTRL_TRIANGLE))
  {
      sceKernelDelayThread(10000);  // wait 10 milliseconds
      sceCtrlReadBufferPositive(&input, 1);
  }
 
  sceKernelExitGame();
  return 0;
}


/* Exit callback */
int exit_callback(int arg1, int arg2, void *common)
{
  sceKernelExitGame();
  return 0;
}


/* Callback thread */
int CallbackThread(SceSize args, void *argp)
{
  int cbid;

  cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
  sceKernelRegisterExitCallback(cbid);

  sceKernelSleepThreadCB();

  return 0;
}


/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void)
{
  int thid = 0;

  thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
  if(thid >= 0)
  {
      sceKernelStartThread(thid, 0, 0);
  }

  return thid;
}

пусть оно и выглядит неказисто зато играет даже VBR. основано на примере с ps2dev, добавлено потоковое декодирование в два типа буфера.

wSlava 02.07.2007 21:12

Еще бы не помешало знать как сделать паузу, ускоренное проигрывание вперед/назад (перемотка), и т.д. А в остальном все гуд.

добавлено через 50 секунд
А ты писал, что с видео разбирался, есть результаты по этому поводу ?

l3VGV 02.07.2007 21:35

Вложений: 1
про остановить, поставить eof в 1 :) перемотка это управление указателем мп3 файла, подробнее сказать затрудняюсь, для не VBR вроде всё просто но надо пробовать. пауза возникнет если не устанавливать donext в 1. тут конечно надо всё переписать на семафоры, но для проги примера и так потянет. я щас озадачился сделать простой движок для проигрывания файлов мр3, мр4 и wav(pcm). вот там всё будет. работы потихоньку ведутся.



докладываю по видео.
есть рабочий пример как разобрать и проиграть pmf, от mp4 отличается как я понял только другим форматом звуковой дорожки. точно также как и тут использует только кишки psp, всё работает очень шустро. архив прилагаю, использовать аккуратно, там cpp, и прочие непонятные вещи, афтор говорит что прога иногда виснет. для генерации pmf файлоф надо качать из торентов UMD tools. эти тулсы Sony confidecial такчто ессно проги с ними потом распространять не получится, надо всётаки добивать мр4 чем и занимаюсь.

на форуме кстати не обозначена ситуация с подобными тулзами. может и упоминать нельзя было?

во нафлудил :bb:

l3VGV 08.07.2007 15:18

Вложений: 1
1) прошу переименовать тему в "Играем мр3, мр4(h264), wav(pcm), atrac, aac"

2) простенький пример как играть пцм вав, играется на параметрах консолькой по умолчанию(44.1 кГц частота семплирования, 16бит на семпл), однако не проблема изменить их на выбранный канал благо как извлеч инфу из файла там показано...

следущим примером (аринтеровочно завтра если не разленюсь опять) будет атрас3 и атрак3+.

l3VGV 09.07.2007 22:07

Вложений: 1
Пример тово как можно играть атрак/3/3+, однако тожесамое можно делать и проще, но об этом потом.

l3VGV 20.07.2007 23:55

Вложений: 1
RC альфа беты версии моей либы для играния звукофф. щас мы умеем играть до 8ми звуков одновременно, в данный момент тока вав, мр3, атрак3/+.
ограничения, тока 16bit 44.1кГц причём атрак и мр3 могут быть тока стерео, вавы=моно и стерео.
особо хочется отметить что не получается добиться одновременново качественного воспроизведения чегото отличного от вава...если же забить болтяру на пожирание ресурсов вечным циклом звуковова потока то всё становится идеально. сие есть парадокс для меня, декодирование слежующего фрейма происходит в 5-6 раз быстрее чем отыграет старый, однако видимо хитрость планировщика потоков велика и управление не возвращается достаточно долго чтобы мы пропустили пару семплов в начале и всё закашлялось. воистину тайна великая есмь. как жаль что не владею я джитсу реверс инженеринга :cray:

юзание, подключить хедер инклудом, в мейк фал добавть -ml.o
сырцы пока не выкладываю ибо уж очень стыдно за внутренний хаос :give_rose:
далее всё просто, в начале гденить лепим mlInit() а потом чтобы чтота проиграть mlPlay("ms0:/test.mp3"), можно также хитрее mlPlayEx("ms0:/test.mp3", тутагромкость, COOP), флажок кооп нужен чтобы звук с темже именем играл даже в том случае если онже уже играет. вот. пока больше нифига не работает, в часности зацикливание и кеширование файлов целиком в память. ибо очень меня качество звука занимает чтобы время тратить на остальное всё.
а да кстати остановка, пауза и резюм тоже не работают пока :p как пропускать заголовок мр3 файла я тоже пока не делал, такчто тут тоже могут некоторые не работать...вот атрак&wav зато работает почти на 100% ибо riff.

вобщем велком, жду отзывов :thank_you:

релиз посвящается вСлаве, да прибудет с ним сила довести ИДЕ до совершенства абсолютного!

l3VGV 28.07.2007 21:31

Вложений: 1
Упдейт. теперь играет гладко. полирую всякие мелочи типа пропуска текстов в мр3 :give_rose:

freecod 03.08.2007 04:22

Что-то у меня последний аттач открывает attachment.php и молчит :\
А вообще очень жду релиза. Может заделаю на на этой базе крутой mp3 плагин =) А то mp3_04f юзает libmad и на некоторых mp3 с низким биптрейтом(?) напоминает пакмана с доса на 4 пентиуме: "Мойадреснедоминеулица,мойадрессоветскийсоюз" :\
Есть идея гениального интерфейса к mp3 плагину =)

l3VGV 05.08.2007 17:49

Вложений: 1
во очередная альфа. даже стабильная. даже играет. однако из функция работают тока плей и стоп алл :man_in_love:
в принципе для простеньково плеера хватит.

freecod 06.08.2007 02:16

Не, выдираем все функции из родного плеера =) Кстати, через апи плеера ID3 (теги) получить можно?

l3VGV 06.08.2007 09:03

нет. более того он некоторые даже пропускает некорректно...

l3VGV 03.11.2007 10:25

Времени чтобы уделять его развитию категарически нехватает. пожтому выкладываю полные исходники того что есть на данный момент.

в дальнейшем(читай когда время нарисуется) акцент будете перемещён на поддержку исключительно атрака и мр4 фиксированного фармата. ибо хочу сделать простенькую игрушку а там универсальность ненужна.


http://www.rapidshare.ru/453423

Ilsor 09.08.2008 10:44

А никто не может рассказать, каким образом на PSP звук вообще запускается и как им управлять? Точнее, меня интересуют вот какие вопросы:
1) Как я понимаю, для работы со звуком создаётся канал. Один канал - один звук. То есть, если я запущу два канала одновременно, то они будут микшироваться и накладываться друг на друга?
2) Команда sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL,pspsample,PSP_AUDIO_FORMAT_MONO); принимает в параметре pspsample частоту сигнала или размер буфера мгновенного воспроизведения (т.е. того буфера, в котором происходит микширование)? Я делал int pspsample=PSP_AUDIO_SAMPLE_ALIGN(22050), а потом подавал на sceAudioSetChannelDataLen размер буфера равный pspsample. Вызывал sceAudioOutputBlocking и при воспроизведении возникают "пробелы". Выходит, что это как раз размер буфера микширования. Я правильно понимаю?
3) Если пункт 2 верный, то как можно изменить частоту дискретизации и разрядность звука? Я вот хочу 22050 и 8 бит. Я такой команды не нашёл в pspaudio.h. Или она замаскирована. :)
4) Какой максимальный размер звука можно подавать sceAudioSetChannelDataLen? И вообще, sceAudioOutputBlocking нужен выровненный по 64 байта блок данных или всё равно? В pspaudio.h ничего об этом не сказано, но здесь в примерах я видел, что выделялся выровненный блок.

Просто я написал игрушку и хотел к ней приделать звуковое оформление. Ан нет. Всё не так просто, как в Direct Sound. :(

l3VGV 09.08.2008 11:19

я пытался понять директ саунд. тут наоборот всё прощее. только нету толковых манов.

1) 1 канал = 1 аппаратный канал в звукочипе. каналы могут иметь разные настройки и аппаратно качественно смешаются на выходе. никто не запрещает сделать 10 софтовых каналов миксовать самому и выдавать в 1 аппаратный. что и сделано в сдл либ и многих других.


2) передается туда частота дискретизации. 44.1К, 20.05К и тп.

3) сделать 8бит невозможно, железка знает тока про 16. те точность не регулируется. на вход всегда подается минимум 4*16. частоту менять можно от 8 до 48.

4) для скорости рекомендую давать кусками по 2-4 килобайта и выше. чем больше кусок тем меньше вероятность кряка. максимум вроде 32. не помню точно. блок выдавать ровный и не из кэша. ато иначе он будет неуправляемый.

Ilsor 09.08.2008 12:21

Вложений: 1
Ага. Вот оно как...

Но тогда вот что смущает:

Цитата:

2) передается туда частота дискретизации. 44.1К, 20.05К и тп.
Тогда я не понимаю, почему вот такая вот программа воспроизводит на нормальной скорости test.wav с параметрами 44100x16, даже если я укажу int pspsample=PSP_AUDIO_SAMPLE_ALIGN(11050);

Код:

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <pspaudiolib.h>
#include <pspaudio.h>

#include <math.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

//----------------------------------------------------------------------------------------
PSP_MODULE_INFO("Sound", 0, 1, 1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER|THREAD_ATTR_VFPU);


void dump_threadstatus(void);

bool done=false;

int exit_callback(int arg1,int arg2,void *common)
{
 done=true;
 return(0);
}
void audio_callback(void* buf,unsigned int length,void *userdata)
{
 /*
 sample_t* ubuf=(sample_t*)buf;
 for(unsigned int n=0;n<length;n++)
 {
  ubuf[n].r=255*(n%2);
  ubuf[n].l=255*((int)(n/2));
 }
 */
}

int CallbackThread(SceSize args, void *argp)
{
 int cbid;
 cbid=sceKernelCreateCallback("Exit Callback",exit_callback,NULL);
 sceKernelRegisterExitCallback(cbid);
 sceKernelSleepThreadCB();
 return(0);
}
int SetupCallbacks(void)
{
 int thid = 0;
 thid=sceKernelCreateThread("update_thread",CallbackThread,0x11,0xFA0,0,0);
 if(thid>=0) sceKernelStartThread(thid, 0, 0);
 return(thid);
}
//----------------------------------------------------------------------------------------
//начинаем программу
int main(int argc, char  **argv)
{
 int n;
 int argv_len=strlen(argv[0]);
 //формируем имя файла уровня
 //отматываем до черты
 for(n=argv_len;n>0;n--)
 {
  if (argv[0][n-1]=='/')
  {
  argv[0][n]=0;//обрубаем строку
  break;
  }
 }
 //устанавливаем обработчики
 SetupCallbacks();

 pspAudioInit();

 sceCtrlSetSamplingCycle(0);
 sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
 SceCtrlData pad;

 //получаем канал и устанавливаем его параметры
 int pspsample=PSP_AUDIO_SAMPLE_ALIGN(11050);
 int channel=sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL,pspsample,PSP_AUDIO_FORMAT_MONO);
 if (channel>0)
 {
  unsigned char *Sound=(unsigned char*)memalign(64,1024*1024*2);
  char string[255];
  unsigned char *ptr=Sound;
  sprintf(string,"%stest.wav",argv[0]);
  FILE *file=fopen(string,"rb");
  int length=0;
  while(1)
  {
  unsigned char b;
  if (fread(&b,1,1,file)<=0) break;
  *ptr=b;
  ptr++;
  length++;
  }
  fclose(file);
  int offset=44;
  unsigned char *SampleBuffer=(unsigned char*)malloc(16384);
  while(done==false)
  {
  sceCtrlReadBufferPositive(&pad,1);
  if (pad.Buttons&PSP_CTRL_TRIANGLE) break;
  while(offset<length)
  {
    sceCtrlReadBufferPositive(&pad,1);
    if (pad.Buttons&PSP_CTRL_TRIANGLE) break;
    int sample=16384;
    if (length<=offset+sample) sample=length-offset;
    sceAudioSetChannelDataLen(channel,sample/2);
    memcpy(SampleBuffer,Sound+offset,sample);
    sceAudioOutputBlocking(channel,PSP_AUDIO_VOLUME_MAX,SampleBuffer);
    offset+=sample;
  }
  }
  free(SampleBuffer);
  free(Sound);
  sceAudioChRelease(channel);
 }
 //pspAudioSetChannelCallback(0,audio_callback,NULL);

 pspAudioEnd();
 //выходим из программы
 sceKernelExitGame();
 return(0);
}

Как такое может быть, если меняется частота дискретизации?

Цитата:

блок выдавать ровный и не из кэша.
В смысле, не из кэша? А как выдавать из кэша?

Ilsor 09.08.2008 13:30

И ещё есть занятная проблема. В audio_callback, как я понимаю, нужно заполнять буфер с разделением на правый и левый каналы? Даже если звук моно?

Ilsor 10.08.2008 18:53

Ну вот, сделал-таки библиотеку для запуска звуков в играх. :)

Ilsor 15.08.2008 18:46

Вложений: 1
Представляю мой вариант звуковой библиотеки для игр. Она, впрочем, не позволит играть длительные музыкальные фрагменты, так как все звуки находятся в памяти постоянно.
Итак, как ей пользоваться.
0) Обработкой звука занимается класс CSound, при этом объект cSound создан по умолчанию.
1) Создать функцию обратного вызова:
void audio_callback(void* buf,unsigned int length,void *userdata)
{
cSound.CallBack(buf,length,userdata);
}
2) Привязать эту функцию к классу:
cSound.SetCallBackFunction(audio_callback);
3) Загрузить звук:
cSound.LoadNewSound(FileName,0);//загружаем звуковой файл
0 - это индентификатор звука, т.е. по этому номеру с данным звуком можно будет работать, разумеется, это любое удобное вам число.
4) Запустить звук на выполнение:
cSound.Play(0);

Всё. Одновременно звуков можно запускать довольно много. Т.е. это именно то, что нужно для игры.

Ах да, файлы звуков в собственном примитивном формате - заголовок-длина и сами незапакованные данные. Этот формат делает прилагаемая программа из несжатых wav-файлов с параметрами 44100 на 16 бит, моно. Но это можно переделать при желании.

l3VGV 15.08.2008 18:57

сколько памяти займет 5минутный фоновый трек?

Ilsor 15.08.2008 21:33

44100(частота)x2(байт на выборку)x5(минут)x60 (по 60 секнуд в минуте)= 26 мегабайт в файле и в два раза больше в памяти (т.к. стерео). Их нужно подгружать потихоньку. :)


Текущее время: 22:11. Часовой пояс GMT +3.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2025, vBulletin Solutions, Inc. Перевод: zCarot
PSPx Forum - Сообщество фанатов игровых консолей.