Time Sound::getPlayingOffset() const { if (getStatus() == Stopped) return Time::Zero; if (getStatus() == Paused) return m_pauseOffset; u32 samplePos = ndspChnGetSamplePos(m_channel); return seconds(static_cast<float>(samplePos) / m_buffer->getSampleRate()) + m_playOffset; }
// startMusic: Plays a song with network streaming feature void startMusic(Socket* sock, Music* src) { closeStream = false; src->streamLoop = false; // TODO: Add looping feature songPointer = 0; netSize = 0; u32 ch = 0x08; u32 ch2 = 0x09; bool non_native_encode = false; ThreadFunc streamFunction = streamWAV; u8 tmp_encode; /*if (src->encoding == CSND_ENCODING_VORBIS){ streamFunction = streamOGG; tmp_encode = src->encoding; src->encoding = CSND_ENCODING_PCM16; non_native_encode = true; }*/ int raw_format; if (src->audiotype == 1) raw_format = NDSP_FORMAT_MONO_PCM16; else raw_format = NDSP_FORMAT_STEREO_PCM16; ndspChnReset(ch); ndspChnWaveBufClear(ch); ndspChnSetInterp(ch, NDSP_INTERP_LINEAR); ndspChnSetRate(ch, float(src->samplerate)); ndspChnSetFormat(ch, raw_format); ndspWaveBuf* waveBuf = (ndspWaveBuf*)calloc(1, sizeof(ndspWaveBuf)); createDspBlock(waveBuf, src->bytepersample, src->mem_size, 0, (u32*)src->audiobuf); src->blocks = NULL; populatePurgeTable(src, waveBuf); ndspChnWaveBufAdd(ch, waveBuf); src->tick = osGetTime(); src->wavebuf = waveBuf; src->ch = ch; src->isPlaying = true; src->lastCheck = ndspChnGetSamplePos(ch); src->streamLoop = false; svcCreateEvent(&updateStream,0); cachePackage* pkg = (cachePackage*)malloc(sizeof(cachePackage)); pkg->client = sock; pkg->song = src; svcSignalEvent(updateStream); threadCreate(streamFunction, pkg, 8192, 0x18, 0, true); src->isPlaying = true; }
void streamWAV(void* arg){ // Fetching cachePackage struct from main thread cachePackage* pack = (cachePackage*)arg; while(1) { // Waiting for updateStream event svcWaitSynchronization(updateStream, U64_MAX); svcClearEvent(updateStream); // Close the thread if closeStream event received if(closeStream){ closeStream = false; svcExitThread(); } // Check if the current stream is paused or not Music* src = pack->song; Socket* Client = pack->client; if (src->isPlaying){ // Check if a free buffer is available if (src->wavebuf2 == NULL){ // Check if file reached EOF if (src->audio_pointer >= src->size){ // Check if playback ended if (!ndspChnIsPlaying(src->ch)){ src->isPlaying = false; src->tick = (osGetTime()-src->tick); } continue; } // Swap audiobuffers u8* tmp = src->audiobuf; src->audiobuf = src->audiobuf2; src->audiobuf2 = tmp; // Create a new block for DSP service u32 bytesRead; src->wavebuf2 = (ndspWaveBuf*)calloc(1,sizeof(ndspWaveBuf)); createDspBlock(src->wavebuf2, src->bytepersample, src->mem_size, 0, (u32*)src->audiobuf); populatePurgeTable(src, src->wavebuf2); ndspChnWaveBufAdd(src->ch, src->wavebuf2); socketSend(Client, "exec2:0000"); u32 processedBytes = 0; netSize = 0; while (netSize <= 0) heapRecv(Client, 2048); while (processedBytes < (src->mem_size / 2)){ if (netSize <= 0){ heapRecv(Client, 2048); continue; } if (strncmp((char*)netBuffer, "EOF", 3) == 0) break; memcpy(&streamCache[songPointer + processedBytes], netBuffer, netSize); processedBytes = processedBytes + netSize; heapRecv(Client, 2048); } memcpy(src->audiobuf, &streamCache[songPointer], src->mem_size); if (songPointer == 0) songPointer = src->mem_size / 2; else songPointer = 0; src->audio_pointer = src->audio_pointer + src->mem_size; // Changing endianess if Big Endian if (src->big_endian){ u64 i = 0; while (i < src->mem_size){ u8 tmp = src->audiobuf[i]; src->audiobuf[i] = src->audiobuf[i+1]; src->audiobuf[i+1] = tmp; i=i+2; } } } // Check if a block playback is finished u32 curSample = ndspChnGetSamplePos(src->ch); if (src->lastCheck > curSample){ // Prepare next block src->wavebuf = src->wavebuf2; src->wavebuf2 = NULL; } // Update sample position tick src->lastCheck = curSample; } } }