@@ -786,6 +786,109 @@ void BinaryRecording::writeSpike (int electrodeIndex, const Spike* spike)
786786 increaseEventCounts (rec);
787787}
788788
789+ void BinaryRecording::writeContinuousDataBatch (const int * writeChannels,
790+ const int * realChannels,
791+ const float * const * dataBuffers,
792+ const double * timestampBuffer,
793+ int numChannels,
794+ int numSamples,
795+ int fileIndex)
796+ {
797+ if (numSamples == 0 || numChannels == 0 )
798+ return ;
799+
800+ // Ensure we have enough batch buffer space
801+ if (numSamples > m_batchBufferSamples || numChannels > m_batchBufferChannels)
802+ {
803+ int newSamples = jmax (numSamples, m_batchBufferSamples);
804+ int newChannels = jmax (numChannels, m_batchBufferChannels);
805+
806+ LOGD (" BinaryRecording::writeContinuousDataBatch: Resizing batch buffer to " ,
807+ newChannels, " channels x " , newSamples, " samples" );
808+
809+ m_batchIntBuffer.malloc (newSamples * newChannels);
810+ m_batchBufferSamples = newSamples;
811+ m_batchBufferChannels = newChannels;
812+ }
813+
814+ // Ensure sample number buffer is large enough
815+ if (numSamples > m_bufferSize)
816+ {
817+ m_sampleNumberBuffer.malloc (numSamples);
818+ m_bufferSize = numSamples;
819+ }
820+
821+ // Resize batch pointer arrays if needed
822+ if (m_batchScaleFactors.size () < static_cast <size_t > (numChannels))
823+ {
824+ m_batchScaleFactors.resize (numChannels);
825+ m_batchIntBufferPtrs.resize (numChannels);
826+ }
827+
828+ // Get file and validate
829+ if (fileIndex < 0 || fileIndex >= m_continuousFiles.size () || ! m_continuousFiles[fileIndex])
830+ return ;
831+
832+ // Setup scale factors and output buffer pointers for each channel
833+ // Each channel gets a contiguous region of m_batchIntBuffer
834+ for (int i = 0 ; i < numChannels; i++)
835+ {
836+ m_batchScaleFactors[i] = 1 .0f / getContinuousChannel (realChannels[i])->getBitVolts ();
837+ m_batchIntBufferPtrs[i] = m_batchIntBuffer.getData () + i * numSamples;
838+ }
839+
840+ // Batch convert all channels using SIMD
841+ SIMDConverter::convertFloatToInt16Batch (
842+ dataBuffers,
843+ m_batchIntBufferPtrs.data (),
844+ m_batchScaleFactors.data (),
845+ numChannels,
846+ numSamples);
847+
848+ // Get starting sample position (all channels in a stream have same position)
849+ uint64 startPos = m_samplesWritten[writeChannels[0 ]];
850+
851+ // Write all channels at once using batch interleaving
852+ m_continuousFiles[fileIndex]->writeChannelBatch (
853+ startPos,
854+ m_batchIntBufferPtrs.data (),
855+ numChannels,
856+ numSamples);
857+
858+ // Update samples written for all channels
859+ for (int i = 0 ; i < numChannels; i++)
860+ {
861+ m_samplesWritten.set (writeChannels[i], m_samplesWritten[writeChannels[i]] + numSamples);
862+ }
863+
864+ // Write timestamps (only once per stream, using the first channel)
865+ // Find which channel in this batch is the first channel for this file (channel index 0 within stream)
866+ for (int i = 0 ; i < numChannels; i++)
867+ {
868+ if (m_channelIndexes[writeChannels[i]] == 0 )
869+ {
870+ int64 baseSampleNumber = getLatestSampleNumber (writeChannels[i]);
871+ uint32 streamId = getContinuousChannel (realChannels[i])->getStreamId ();
872+
873+ if (! wroteFirstSampleNumber[streamId])
874+ {
875+ firstSampleNumber[streamId] = baseSampleNumber;
876+ wroteFirstSampleNumber[streamId] = true ;
877+ }
878+
879+ for (int s = 0 ; s < numSamples; s++)
880+ m_sampleNumberBuffer[s] = baseSampleNumber + s;
881+
882+ m_dataTimestampFiles[fileIndex]->writeData (m_sampleNumberBuffer, numSamples * sizeof (int64));
883+ m_dataTimestampFiles[fileIndex]->increaseRecordCount (numSamples);
884+
885+ m_dataSyncTimestampFiles[fileIndex]->writeData (timestampBuffer, numSamples * sizeof (double ));
886+ m_dataSyncTimestampFiles[fileIndex]->increaseRecordCount (numSamples);
887+ break ;
888+ }
889+ }
890+ }
891+
789892void BinaryRecording::writeTimestampSyncText (uint64 streamId, int64 sampleNumber, float sourceSampleRate, String text)
790893{
791894 if (! m_syncTextFile)
0 commit comments