Homebrew Homebrew Development

trainboy2019

Well-Known Member
Member
Joined
Oct 6, 2015
Messages
1,114
Trophies
0
Age
23
Location
GA
XP
1,107
Country
United States
Here's a simplified version of my code.
Code:
#include <string>

#include <3ds.h>
#include <sf2d.h>
#include <sfil.h>

#include "topscr_png.h"

using namespace std;

int main() {
    aptInit();
    sf2d_init();
    sf2d_texture* topScreen       = sfil_load_PNG_buffer(topscr_png,        SF2D_PLACE_RAM);
    // Main loop
    while (aptMainLoop()) {
        hidScanInput();
        u32 kDown = hidKeysDown();
        u32 kHeld = hidKeysHeld();
        u32 kUp = hidKeysUp();
        if (kDown & KEY_START) {
            break;
        }
        // draw instructions
        sf2d_start_frame(GFX_TOP, GFX_LEFT);
        sf2d_draw_texture(topScreen, 0, 0);
        sf2d_end_frame();
        sf2d_swapbuffers();
    }
    sf2d_free_texture(topScreen);
    sf2d_fini();
    aptExit();
    return 0;
}
 

nop90

Well-Known Member
Member
Joined
Jan 11, 2014
Messages
1,556
Trophies
0
Location
Rome
XP
3,136
Country
Italy
Here's a simplified version of my code.
...

Your example works fine for me.

Attached you can find the whole project, with a compiled 3dsx. Test it on your 3ds, and if has the same problem, than the reason is in your CFW/entryoint.

If the compiled 3dsx works on your 3ds, compile again my sources and check if it has the same behaviour. If not the problem in in your toolchain.
 

Attachments

  • test1.zip
    2 MB · Views: 146
D

Deleted User

Guest
Just a question out of curiosity; are there any good debugging tools for the 3DS? I know there's Luma's Exception Handler, but it seems fairly low level, and more often than not, I find myself just disabling it and putting a bunch of print messages in my code.
 

nop90

Well-Known Member
Member
Joined
Jan 11, 2014
Messages
1,556
Trophies
0
Location
Rome
XP
3,136
Country
Italy
Just a question out of curiosity; are there any good debugging tools for the 3DS? I know there's Luma's Exception Handler, but it seems fairly low level, and more often than not, I find myself just disabling it and putting a bunch of print messages in my code.
I do rhe same. Lot of printf and move a breakpoint (while(1)) in the code flow to check that everything works or to find a crash point.
 

trainboy2019

Well-Known Member
Member
Joined
Oct 6, 2015
Messages
1,114
Trophies
0
Age
23
Location
GA
XP
1,107
Country
United States
Your example works fine for me.

Attached you can find the whole project, with a compiled 3dsx. Test it on your 3ds, and if has the same problem, than the reason is in your CFW/entryoint.

If the compiled 3dsx works on your 3ds, compile again my sources and check if it has the same behaviour. If not the problem in in your toolchain.
It still crashes to the home menu. What's your entry point? I'm using b9s 1.2 with luma 8.1 and using the new hb menu injected into download play via rosalina.
 

Jokshoks

New Member
Newbie
Joined
Aug 7, 2017
Messages
1
Trophies
0
Age
26
XP
41
Country
Netherlands
Hey guys, i have an old 3ds on 11.4... i was wondering, is it possible to downgrade it to 11.3? Or is there allready a hombrew hack for 11.5? Consinder me a newby with this
 

trainboy2019

Well-Known Member
Member
Joined
Oct 6, 2015
Messages
1,114
Trophies
0
Age
23
Location
GA
XP
1,107
Country
United States
Hey guys, i have an old 3ds on 11.4... i was wondering, is it possible to downgrade it to 11.3? Or is there allready a hombrew hack for 11.5? Consinder me a newby with this
Do you have CFW? If not, I'm pretty sure you're SOL at the moment.
For the O3ds, there are no payloads, so the home-brew launcher won't work.
The only way to get CFW on 11.4 right now is to get a hardmod.
 
D

Deleted User

Guest
Alright, so, in order to get a better understanding of the 3DS's sound systems (and sound systems in general) while also trying to contribute to the community, I've been working on a sound library for use in games. Much of the code in said sound library is based off the sound systems in ctrulua (namely, this: https://github.com/ctruLua/ctruLua/blob/master/source/audio.c).

Here's the link to what I've made thus far: https://github.com/BEPISMAN2/sound-thing

While simply loading sounds into memory and playing them works beautifully, I've found trouble trying to get streaming to work. While streaming from the file technically works, the audio quality is horribly choppy. I've tried comparing my code to the code found in ctrulua several times, and for the life of me, I can't seem to understand what's causing the problem.

Here's a snippet from the streaming code:
Code:
       // next chunk has started to play, load it into memory
       if (stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) == stream->nextWaveBuf->sequence_id) {
           stream->prevStartTime = stream->prevStartTime + (double) (wav->chunkNSamples) / wav->rate;
           
           if (!stream->eof) {
               //printf("loading next chunk into memory...\n");
               // swap these buffers, set nextData (about to play) to prevData (playing now)
               char *nextData = stream->nextData;
               char *prevData = stream->prevData;
               stream->prevData = nextData;
               stream->nextData = prevData;
               stream->prevWaveBuf = stream->nextWaveBuf;
               
               // recalculate chunk size and number of samples
               u32 chunkSize = fmin(wav->fileSize - stream->filePos, wav->chunkSize);
               u32 chunkNSamples = chunkSize / wav->channels / wav->bytePerSample;
               
               // read next chunk into memory (will play after this chunk is done playing)
               fseek(wav->file, stream->filePos, SEEK_SET);
               fread(stream->nextData, chunkSize, 1, wav->file);
               stream->filePos = ftell(wav->file);
               if (stream->filePos == wav->fileSize) {
                   stream->eof = true;
               }
               
               //printf("filepos: %d\n", stream->filePos);
               
               // create new wavebuf
               ndspWaveBuf *wbuf = calloc(1, sizeof(ndspWaveBuf));
               wbuf->data_vaddr = stream->nextData;
               wbuf->nsamples = chunkNSamples;
               wbuf->looping = false;
               
               // flush that shit
               DSP_FlushDataCache((u32*)stream->nextData, chunkSize);
               ndspChnWaveBufAdd(i, wbuf);
               
               // set next wave buf
               stream->nextWaveBuf = wbuf;
               //printf("Next chunk loaded into memory.\n");
           }
       }

Any help would be appreciated.
 

nop90

Well-Known Member
Member
Joined
Jan 11, 2014
Messages
1,556
Trophies
0
Location
Rome
XP
3,136
Country
Italy
Alright, so, in order to get a better understanding of the 3DS's sound systems (and sound systems in general) while also trying to contribute to the community, I've been working on a sound library for use in games. Much of the code in said sound library is based off the sound systems in ctrulua (namely, this: https://github.com/ctruLua/ctruLua/blob/master/source/audio.c).

Here's the link to what I've made thus far: https://github.com/BEPISMAN2/sound-thing

While simply loading sounds into memory and playing them works beautifully, I've found trouble trying to get streaming to work. While streaming from the file technically works, the audio quality is horribly choppy. I've tried comparing my code to the code found in ctrulua several times, and for the life of me, I can't seem to understand what's causing the problem.

Here's a snippet from the streaming code:
Code:
       // next chunk has started to play, load it into memory
       if (stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) == stream->nextWaveBuf->sequence_id) {
           stream->prevStartTime = stream->prevStartTime + (double) (wav->chunkNSamples) / wav->rate;
          
           if (!stream->eof) {
               //printf("loading next chunk into memory...\n");
               // swap these buffers, set nextData (about to play) to prevData (playing now)
               char *nextData = stream->nextData;
               char *prevData = stream->prevData;
               stream->prevData = nextData;
               stream->nextData = prevData;
               stream->prevWaveBuf = stream->nextWaveBuf;
              
               // recalculate chunk size and number of samples
               u32 chunkSize = fmin(wav->fileSize - stream->filePos, wav->chunkSize);
               u32 chunkNSamples = chunkSize / wav->channels / wav->bytePerSample;
              
               // read next chunk into memory (will play after this chunk is done playing)
               fseek(wav->file, stream->filePos, SEEK_SET);
               fread(stream->nextData, chunkSize, 1, wav->file);
               stream->filePos = ftell(wav->file);
               if (stream->filePos == wav->fileSize) {
                   stream->eof = true;
               }
              
               //printf("filepos: %d\n", stream->filePos);
              
               // create new wavebuf
               ndspWaveBuf *wbuf = calloc(1, sizeof(ndspWaveBuf));
               wbuf->data_vaddr = stream->nextData;
               wbuf->nsamples = chunkNSamples;
               wbuf->looping = false;
              
               // flush that shit
               DSP_FlushDataCache((u32*)stream->nextData, chunkSize);
               ndspChnWaveBufAdd(i, wbuf);
              
               // set next wave buf
               stream->nextWaveBuf = wbuf;
               //printf("Next chunk loaded into memory.\n");
           }
       }

Any help would be appreciated.

The main problem should be that reading from sd is very slow (slower with 3dsx executable than with code installed from a CIA). It's better lo load in memory a compressed sound file (ogg, mp3, ...) and decode it chunk by chunk when needed.

Other things I learned working on the audio driver of the SDL lib are:
- The audio buffer feeding loop should be in a separate thread running with higher priority than the program code
- If you use the GPU, the drawing code needs to run on a therad with higher priority than the sound thread to avoid flickering
- never run threads with the highest priority (0x18), or you'll interfere with the NDSP handler thread started by ctrulib calling ndspInit();
 
D

Deleted User

Guest
The main problem should be that reading from sd is very slow (slower with 3dsx executable than with code installed from a CIA). It's better lo load in memory a compressed sound file (ogg, mp3, ...) and decode it chunk by chunk when needed.

Other things I learned working on the audio driver of the SDL lib are:
- The audio buffer feeding loop should be in a separate thread running with higher priority than the program code
- If you use the GPU, the drawing code needs to run on a therad with higher priority than the sound thread to avoid flickering
- never run threads with the highest priority (0x18), or you'll interfere with the NDSP handler thread started by ctrulib calling ndspInit();
Alright. I tried running the audio buffer feeding loop in a separate thread with a priority 2 greater than the current thread. However, once I did this, all calls to DSP_FlushDataCache fail with an error code of 0xE0E01BF5.

Here's the code:
Code:
gfxInitDefault();
   ndspInit();
   consoleInit(GFX_TOP, NULL);
   
   wavFile wav;
   Result ret;
   
   s32 prio;
   svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
   printf("The priority for the main thread is 0x%lx. The sound thread will run above this.\n", prio);
   
   ret = loadWav("/test.wav", &wav, 0.1);
   if (ret != 0)
       goto exit;
   
   printWav(&wav);
   
   if (prio + 2 == 0x18)
       prio = 0x17;
   
   soundThread = threadCreate(updateChannels, NULL, 2 * sizeof(channels), prio + 2, -2, false);
   printf("thread created at %p\n", &soundThread);
   
   printf("Press the START button to start playback.\n");
   waitForInput();
   
   ret = playWav(&wav, 0, false);
   if (ret != 0) {
       fprintf(stderr, "error: could not play file, result: %x\n", ret);
       deleteWav(&wav);
       goto exit;
   }

and for flushing the data cache. This runs in the main thread; will this cause problems?

Code:
Result playWav(wavFile *wav, int channel, bool loop) {
   ndspWaveBuf *wbuf;
   
   stopWav(channel);
   ndspChnReset(channel);
   ndspChnSetRate(channel, wav->rate);
   ndspChnInitParams(channel);
   ndspChnSetFormat(channel, NDSP_CHANNELS(wav->channels) | NDSP_ENCODING(wav->encoding));
   
   wbuf = calloc(1, sizeof(ndspWaveBuf));
   
   wbuf->data_vaddr = wav->data;
   wbuf->nsamples = wav->chunkNSamples;
   wbuf->looping = (wav->chunkSize < wav->size) ? false : loop;
   
   Result ret = DSP_FlushDataCache((u32*)wav->data, wav->chunkSize);
   ndspChnWaveBufAdd(channel, wbuf);
   
   channels[channel] = wav;

   // free previous stream
   if (streaming[channel] != NULL) {
       free(streaming[channel]);
       streaming[channel] = NULL;
   }
   
   // create new stream
   if (wav->chunkSize < wav->fileSize) {
       audioStream *stream = calloc(1, sizeof(audioStream));
       stream->loop = loop;
       stream->audio = wav;
       stream->nextWaveBuf = wbuf;
   
       if (linearSpaceFree() < wav->chunkSize * 2) {
           fprintf(stderr, "error: not enough linear memory available\n");
           return -1;
       }
   
       stream->nextData = linearAlloc(wav->chunkSize);
       stream->prevData = linearAlloc(wav->chunkSize);
       stream->filePos = wav->filePos;
       stream->done = false;
       streaming[channel] = stream;
   }
   
   return ret;
}
 

elhobbs

Well-Known Member
Member
Joined
Jul 28, 2008
Messages
1,044
Trophies
1
XP
3,032
Country
United States
Alright. I tried running the audio buffer feeding loop in a separate thread with a priority 2 greater than the current thread. However, once I did this, all calls to DSP_FlushDataCache fail with an error code of 0xE0E01BF5.

Here's the code:
Code:
gfxInitDefault();
   ndspInit();
   consoleInit(GFX_TOP, NULL);
  
   wavFile wav;
   Result ret;
  
   s32 prio;
   svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
   printf("The priority for the main thread is 0x%lx. The sound thread will run above this.\n", prio);
  
   ret = loadWav("/test.wav", &wav, 0.1);
   if (ret != 0)
       goto exit;
  
   printWav(&wav);
  
   if (prio + 2 == 0x18)
       prio = 0x17;
  
   soundThread = threadCreate(updateChannels, NULL, 2 * sizeof(channels), prio + 2, -2, false);
   printf("thread created at %p\n", &soundThread);
  
   printf("Press the START button to start playback.\n");
   waitForInput();
  
   ret = playWav(&wav, 0, false);
   if (ret != 0) {
       fprintf(stderr, "error: could not play file, result: %x\n", ret);
       deleteWav(&wav);
       goto exit;
   }

and for flushing the data cache. This runs in the main thread; will this cause problems?

Code:
Result playWav(wavFile *wav, int channel, bool loop) {
   ndspWaveBuf *wbuf;
  
   stopWav(channel);
   ndspChnReset(channel);
   ndspChnSetRate(channel, wav->rate);
   ndspChnInitParams(channel);
   ndspChnSetFormat(channel, NDSP_CHANNELS(wav->channels) | NDSP_ENCODING(wav->encoding));
  
   wbuf = calloc(1, sizeof(ndspWaveBuf));
  
   wbuf->data_vaddr = wav->data;
   wbuf->nsamples = wav->chunkNSamples;
   wbuf->looping = (wav->chunkSize < wav->size) ? false : loop;
  
   Result ret = DSP_FlushDataCache((u32*)wav->data, wav->chunkSize);
   ndspChnWaveBufAdd(channel, wbuf);
  
   channels[channel] = wav;

   // free previous stream
   if (streaming[channel] != NULL) {
       free(streaming[channel]);
       streaming[channel] = NULL;
   }
  
   // create new stream
   if (wav->chunkSize < wav->fileSize) {
       audioStream *stream = calloc(1, sizeof(audioStream));
       stream->loop = loop;
       stream->audio = wav;
       stream->nextWaveBuf = wbuf;
  
       if (linearSpaceFree() < wav->chunkSize * 2) {
           fprintf(stderr, "error: not enough linear memory available\n");
           return -1;
       }
  
       stream->nextData = linearAlloc(wav->chunkSize);
       stream->prevData = linearAlloc(wav->chunkSize);
       stream->filePos = wav->filePos;
       stream->done = false;
       streaming[channel] = stream;
   }
  
   return ret;
}
The ndsp code does not seem to like being called from a separate thread. Another issue is that the 3ds timer is not very accurate. When streaming I have found it to be beneficial to use a lot of small buffers and to measure playback position as a function of completed buffers. It is fine to use a separate thread for decoding and filling buffers but let the main thread submit them. And if you do use a thread then make sure it sleeps.
 
D

Deleted User

Guest
The ndsp code does not seem to like being called from a separate thread. Another issue is that the 3ds timer is not very accurate. When streaming I have found it to be beneficial to use a lot of small buffers and to measure playback position as a function of completed buffers. It is fine to use a separate thread for decoding and filling buffers but let the main thread submit them. And if you do use a thread then make sure it sleeps.
Alright; so the way I see it, the audio buffer feeding should be run at a higher priority than the main thread, but all of the ndsp functions should be called from the main thread itself?

So, hypothetically, let's set a variable:
Code:
bool readNextBuffer[24];

and let's define the function in which the buffer is read as
Code:
void updateBuffers(int channel) {
        if (readNextBuffer) {
            // swap buffers, read next buffer into memory
            readNextBuffer[channel] = false;
        }
}

We run updateBuffers() in a thread, like so:
Code:
Thread bufferThreads
for i in range(24):
    Thread bufferThread = threadCreate(updateBuffers, i, STACKSIZE, prio + 1, -2, false);

We also have some code which handles all the ndsp functions, which runs on the main thread:
Code:
void checkBuffers() {
    for (int i = 0; i < 24; i++) {
        // read current stream into memory
        if (stream->prevWaveBuf == NULL && stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) != stream->nextWaveBuf->sequence_id && stream->eof == true) {
            readNextBuffer[i] = true
            threadJoin(bufferThread);

            DSP_FlushDataCache();
            ndspChnWaveBufAdd();
        }
    }
}

So, essentially, if the checkBuffers function (run in the main thread) detects that the next chunk should be loaded into memory, it sets the readNextBuffer flag to true and waits for the next chunk to be loaded into memory, then makes all the necessary ndsp calls back in the main thread. Should this work? I'm rather new to threading, so I'm not sure if this approach is safe or not. Also, where would sleeping for each thread fall into this?
 

elhobbs

Well-Known Member
Member
Joined
Jul 28, 2008
Messages
1,044
Trophies
1
XP
3,032
Country
United States
Alright; so the way I see it, the audio buffer feeding should be run at a higher priority than the main thread, but all of the ndsp functions should be called from the main thread itself?

So, hypothetically, let's set a variable:
Code:
bool readNextBuffer[24];

and let's define the function in which the buffer is read as
Code:
void updateBuffers(int channel) {
        if (readNextBuffer) {
            // swap buffers, read next buffer into memory
            readNextBuffer[channel] = false;
        }
}

We run updateBuffers() in a thread, like so:
Code:
Thread bufferThreads
for i in range(24):
    Thread bufferThread = threadCreate(updateBuffers, i, STACKSIZE, prio + 1, -2, false);

We also have some code which handles all the ndsp functions, which runs on the main thread:
Code:
void checkBuffers() {
    for (int i = 0; i < 24; i++) {
        // read current stream into memory
        if (stream->prevWaveBuf == NULL && stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) != stream->nextWaveBuf->sequence_id && stream->eof == true) {
            readNextBuffer[i] = true
            threadJoin(bufferThread);

            DSP_FlushDataCache();
            ndspChnWaveBufAdd();
        }
    }
}

So, essentially, if the checkBuffers function (run in the main thread) detects that the next chunk should be loaded into memory, it sets the readNextBuffer flag to true and waits for the next chunk to be loaded into memory, then makes all the necessary ndsp calls back in the main thread. Should this work? I'm rather new to threading, so I'm not sure if this approach is safe or not. Also, where would sleeping for each thread fall into this?
24 threads? No. Keep in mind the 3ds uses preemptive multitasking.
Certainly not the cleanest code or even the best approach, but here is an example. The dsp_* functions deal with handling a streaming buffer. Which in this case is software mixed sound effects.
https://github.com/elhobbs/hexen/blob/master/3ds/3ds_sound.c

And again here for mus synthesis. This example does use a thread but you will see a few extra sleeps thrown in to get it to play nice(-ish).
https://github.com/elhobbs/hexen/blob/master/3ds/musplay.c (toward the bottom)
 

agoidz

New Member
Newbie
Joined
Aug 10, 2017
Messages
3
Trophies
0
Age
27
XP
52
Country
Philippines
The purpose of this thread it to help with homebrew development by centralizing all progress, and having a place to discuss development.

Getting started: http://wiki.gbatemp.net/wiki/3DS_Homebrew_Development

You can find a list of current released 3DS homebrew on Wikitemp.
If one is missing, feel free to add it to maintain a list of existing releases.
http://wiki.gbatemp.net/wiki/List_of_3DS_homebrew


Examples of released homebrew:


Rop Loaders

fierce waffle's ROP Loader

Alternate Rop Installer

Rop Multi-loader(use this one)


Development

fierce waffle's RAM dumper

Python Tools for 3DS

3DS_Homebrew_Stuff2

Modified build.py for GNU/Linux

ctrulib


Homebrew

BlargSnes

3DNES

Mandelbrot homebrew

nop90's 3DS_Homebrew

homebrew screen test

yeti3DS
The purpose of this thread it to help with homebrew development by centralizing all progress, and having a place to discuss development.

Getting started: http://wiki.gbatemp.net/wiki/3DS_Homebrew_Development

You can find a list of current released 3DS homebrew on Wikitemp.
If one is missing, feel free to add it to maintain a list of existing releases.
http://wiki.gbatemp.net/wiki/List_of_3DS_homebrew


Examples of released homebrew:


Rop Loaders

fierce waffle's ROP Loader

Alternate Rop Installer

Rop Multi-loader(use this one)


Development

fierce waffle's RAM dumper

Python Tools for 3DS

3DS_Homebrew_Stuff2

Modified build.py for GNU/Linux

ctrulib


Homebrew

BlargSnes

3DNES

Mandelbrot homebrew

nop90's 3DS_Homebrew

homebrew screen test

yeti3DS
Hi 3ds have OTG???
That would be awesome :wub: , i also work only in linux ( just in my work office with windows :nayps3:)
Also i would like to know the developers are writing their libs ...........like blindly ? trying to find what "this/that" does ? :blink: (reverse engineering ?)
 

nop90

Well-Known Member
Member
Joined
Jan 11, 2014
Messages
1,556
Trophies
0
Location
Rome
XP
3,136
Country
Italy
Alright. I tried running the audio buffer feeding loop in a separate thread with a priority 2 greater than the current thread. However, once I did this, all calls to DSP_FlushDataCache fail with an error code of 0xE0E01BF5.

Sorry for aswering so late, I'm not at home and can connect only at night with my mobile.

About threads priority, the lower the priority value, the higher the thread priority.

So you have to subtract something (not adding) to the value you get with svcGetThreadPriority().

This should solve the exception caused flushing memory from the created thread (I don't have problems calling dsp functions from other theads than the main one)

Now I don't have time to check all your code, but let me know if with this change something works better.
 

elhobbs

Well-Known Member
Member
Joined
Jul 28, 2008
Messages
1,044
Trophies
1
XP
3,032
Country
United States
Sorry for aswering so late, I'm not at home and can connect only at night with my mobile.

About threads priority, the lower the priority value, the higher the thread priority.

So you have to subtract something (not adding) to the value you get with svcGetThreadPriority().

This should solve the exception caused flushing memory from the created thread (I don't have problems calling dsp functions from other theads than the main one)

Now I don't have time to check all your code, but let me know if with this change something works better.
In regards to threads - if you stop a channel and then try to play a new sound on that same channel from a thread then it is not going to start correctly unless you put sleep call in between the stop and start.
 

nop90

Well-Known Member
Member
Joined
Jan 11, 2014
Messages
1,556
Trophies
0
Location
Rome
XP
3,136
Country
Italy
In regards to threads - if you stop a channel and then try to play a new sound on that same channel from a thread then it is not going to start correctly unless you put sleep call in between the stop and start.

Maybe the hint is for @B_E_P_I_S_M_A_N

In my code usually I use only a channel with two buffer, filling a buffer with new samples while the other is playing and then adding it to the channel queue.

I don't need to stop the channel since there are samples to play.
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    Xdqwerty @ Xdqwerty: Where's everybody?