{"version":3,"sources":["images/chrome-icon.svg","webpack:///./src/services/atracdenc-worker.ts?dc3c","serviceWorker.ts","utils.ts","services/netmd.ts","services/registry.ts","redux/upload-dialog-feature.ts","redux/rename-dialog-feature.ts","redux/error-dialog-feature.ts","redux/convert-dialog-feature.ts","redux/dump-dialog-feature.ts","redux/record-dialog-feature.ts","redux/app-feature.ts","redux/main-feature.ts","redux/store.ts","redux/actions.ts","components/about-dialog.tsx","components/topmenu.tsx","components/welcome.tsx","components/rename-dialog.tsx","components/upload-dialog.tsx","components/record-dialog.tsx","components/error-dialog.tsx","components/convert-dialog.tsx","components/controls.tsx","components/dump-dialog.tsx","components/main.tsx","services/atracdenc-worker.ts","components/app.tsx","services/audio-export.ts","index.tsx","services/mediarecorder.ts"],"names":["module","exports","Worker","isLocalhost","Boolean","window","location","hostname","match","registerValidSW","swUrl","config","navigator","serviceWorker","register","then","registration","onupdatefound","installingWorker","installing","onstatechange","console","log","state","controller","onUpdate","onSuccess","update","catch","error","sleep","ms","Promise","resolve","setTimeout","async","sleepWithProgressCallback","cb","elapsedSecs","interval","setInterval","Math","min","clearInterval","useShallowEqualSelector","selector","useSelector","shallowEqual","getPublicPathFor","script","sanitizeTitle","title","normalize","replace","require","ServiceRegistry","slice","createSlice","name","initialState","visible","writtenProgress","encryptedProgress","totalProgress","trackTotal","trackConverting","trackCurrent","titleCurrent","titleConverting","reducers","setVisible","action","payload","setWriteProgress","encrypted","written","total","setTrackProgress","current","converting","reducer","actions","enableBatching","index","setCurrentName","setIndex","setErrorMessage","format","setFormat","inputDeviceId","setInputDeviceId","trackDone","setProgress","mainView","loading","pairingFailed","pairingMessage","browserSupported","darkMode","key","defaultValue","res","localStorage","getItem","JSON","parse","e","loadPreference","aboutDialogVisible","setState","setLoading","setPairingFailed","setPairingMessage","setBrowserSupported","setDarkMode","value","setItem","stringify","showAboutDialog","disc","deviceName","setDisc","setDeviceName","store","configureStore","renameDialog","uploadDialog","errorDialog","convertDialog","dumpDialog","recordDialog","appState","main","middleware","getDefaultMiddleware","listContent","dispatch","appStateActions","serviceRegistry","netmdService","getDeviceName","batchActions","mainActions","WireformatDict","SP","Wireformat","pcm","LP2","lp2","LP105","l105kbps","LP4","lp4","convertAndUpload","files","getState","audioExportService","wireformat","uploadDialogActions","updateProgressCallback","trackUpdate","length","updateTrack","maxTitleLength","getTracks","map","track","reduce","acc","floor","errorMessage","i","item","converted","convertNext","f","push","reject","data","prepare","export","file","err","j","conversionIterator","extStartIndex","lastIndexOf","substring","upload","actionToDispatch","concat","errorDialogAction","Transition","React","forwardRef","props","ref","Slide","direction","AboutDialog","useDispatch","Dialog","open","maxWidth","fullWidth","TransitionComponent","aria-labelledby","DialogTitle","id","DialogContent","DialogContentText","Link","rel","href","target","DialogActions","Button","onClick","appActions","TopMenu","useState","menuAnchorEl","setMenuAnchorEl","menuOpen","handleMenuClose","menuItems","MenuItem","renameDialogActions","wipeDisc","Fragment","IconButton","aria-label","aria-controls","aria-haspopup","event","currentTarget","Menu","anchorEl","keepMounted","onClose","useStyles","makeStyles","theme","position","flex","display","justifyContent","flexDirection","alignItems","button","marginTop","spacing","minWidth","chromeLogo","width","height","why","alignSelf","headBox","Welcome","classes","toLowerCase","showWhyUnsupported","setWhyUnsupported","Box","className","Typography","component","variant","align","color","init","connect","pair","message","FormControl","style","visibility","FormHelperText","preventDefault","alt","src","ChromeIconPath","RenameDialog","renameDialogVisible","renameDialogTitle","renameDialogIndex","what","handleCancelRename","handleDoRename","newName","renameDisc","renameTrack","TextField","autoFocus","label","type","onKeyDown","onChange","progressPerc","progressBar","uploadLabel","UploadDialog","progressValue","bufferValue","convertedValue","aria-describedby","LinearProgress","valueBuffer","RecordDialog","round","ErrorDialog","errorDialogActions","container","formControl","ConvertDialog","handleClose","convertDialogActions","InputLabel","Select","labelId","ev","input","Input","Controls","handlePrev","useCallback","prev","handlePlay","play","handleStop","stop","handleNext","next","marginRight","selectEmpty","head","textShadow","fontSize","typography","h2","textAlign","marginBottom","DumpDialog","trackIndexes","devices","setDevices","mediaRecorderService","stopTestInput","dumpDialogActions","handleChange","deviceId","playTestInput","handleStartTransfer","indexes","recordDialogAction","tracks","filter","t","indexOf","entries","gotoTrack","getPosition","expected","every","_","pause","initStream","startRecording","durationInSec","duration","perc","stopRecording","downloadRecorded","closeStream","useEffect","mediaDevices","getUserMedia","audio","inputDevices","enumerateDevices","device","kind","updateDeviceList","displayEmpty","disabled","add","bottom","right","overflowY","marginLeft","outline","toolbar","breakpoints","up","toolbarLabel","toolbarHighlight","palette","secondary","backgroundColor","lighten","light","text","primary","dark","formatBadge","BadgeImpl","badge","colorPrimary","border","background","paper","padding","titleCell","overflow","textOverflow","indexCell","whiteSpace","paddingRight","backdrop","zIndex","drawer","EncodingName","Encoding","sp","Main","selected","setSelected","selectedCount","moveMenuAnchorEl","setMoveMenuAnchorEl","handleShowMoveMenu","handleCloseMoveMenu","handleMoveSelectedTrack","destIndex","srcIndex","moveTrack","handleShowDumpDialog","uploadedFiles","setUploadedFiles","onDrop","acceptedFiles","rejectedFiles","getRootProps","getInputProps","isDragActive","useDropzone","accept","noClick","group","groups","encoding","formatTimeFromFrames","sort","l","r","handleRenameDoubleClick","selectedIndex","currentName","find","left","Toolbar","clsx","Checkbox","indeterminate","checked","inputProps","Tooltip","PaperProps","maxHeight","Array","fill","reverse","deleteTrack","Table","size","TableHead","TableRow","TableCell","TableBody","hover","includes","onDoubleClick","handleSelectClick","Backdrop","Fab","Module","layout","copyright","down","flexWrap","minidiscLogo","controlsContainer","paddingLeft","order","darkTheme","createMuiTheme","contrastText","lightTheme","App","ThemeProvider","CssBaseline","Paper","undefined","Date","getFullYear","data-via","data-hashtags","data-dnt","data-show-count","CircularProgress","AtracdencProcess","worker","messageCallback","onmessage","this","handleMessage","bind","postMessage","bitrate","eventData","result","terminate","WorkerGlobalScope","self","a","others","importScripts","m","setLogger","msg","stream","dataArray","Uint8Array","FS","writeFile","callMain","fileStat","stat","tmp","outAt3FileStream","read","close","buffer","AtracdencWorker","FFMpegAudioExportService","ffmpegProcess","atracdencProcess","loglines","inFileName","outFileNameNoExt","setLogging","createWorker","logger","corePath","workerPath","load","ext","split","Error","write","transcode","audioFormatRegex","inputFormatRegex","line","outFileName","encode","constructor","debug","netmdInterface","_fn","args","method","info","warn","child","iface","openNewDevice","usb","openPairedDevice","netMd","finalize","setTrackTitle","oldName","getDiscTitle","newNameWithGroups","_getDiscTitle","cacheTOC","setDiscTitle","syncTOC","eraseTrack","eraseDisc","dst","progressCallback","byteLength","updateProgress","w","webWorkerAsyncPacketIterator","makeGetAsyncPacketIteratorOnWorkerThread","encryptedBytes","mdTrack","MDTrack","download","writtenBytes","nextTrack","previousTrack","recorder","audioContext","analyserNode","gainNode","AudioContext","createGain","createAnalyser","createMediaStreamSource","destination","recordConstraints","autoGainControl","channelCount","echoCancellation","noiseSuppression","sampleRate","highpassFilter","audioTracks","getAudioTracks","getSettings","Recorder","record","forEach","exportWAV","url","URL","createObjectURL","document","createElement","body","appendChild","click","revokeObjectURL","removeChild","deferredPrompt","addEventListener","returnValue","ondisconnect","ReactDOM","render","getElementById","process","origin","fetch","headers","response","contentType","get","status","ready","unregister","reload","checkValidServiceWorker"],"mappings":"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","file":"static/js/main.10b5445b.chunk.js","sourcesContent":["module.exports = __webpack_public_path__ + \"static/media/chrome-icon.f3b6c54c.svg\";","module.exports = function() {\n return new Worker(__webpack_public_path__ + \"2fad141346134fe4e0c3.worker.js\");\n};","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.0/8 are considered localhost for IPv4.\n window.location.hostname.match(/^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)\n);\n\ntype Config = {\n onSuccess?: (registration: ServiceWorkerRegistration) => void;\n onUpdate?: (registration: ServiceWorkerRegistration) => void;\n};\n\nexport function register(config?: Config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/final-service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl: string, config?: Config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n console.log('state change', installingWorker.state);\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n registration.update(); // Check for new version everytime we load the page\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl: string, config?: Config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl, {\n headers: { 'Service-Worker': 'script' },\n })\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log('No internet connection found. App is running in offline mode.');\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import { useSelector, shallowEqual } from 'react-redux';\nimport { RootState } from './redux/store';\n\nexport function sleep(ms: number) {\n return new Promise(resolve => {\n setTimeout(resolve, ms);\n });\n}\n\nexport async function sleepWithProgressCallback(ms: number, cb: (perc: number) => void) {\n let elapsedSecs = 1;\n let interval = setInterval(() => {\n elapsedSecs++;\n cb(Math.min(100, ((elapsedSecs * 1000) / ms) * 100));\n }, 1000);\n await sleep(ms);\n window.clearInterval(interval);\n}\n\nexport function useShallowEqualSelector(selector: (state: TState) => TSelected): TSelected {\n return useSelector(selector, shallowEqual);\n}\n\nexport function debugEnabled() {\n return process.env.NODE_ENV === 'development';\n}\n\nexport function getPublicPathFor(script: string) {\n return `${process.env.PUBLIC_URL}/${script}`;\n}\n\nexport function savePreference(key: string, value: unknown) {\n localStorage.setItem(key, JSON.stringify(value));\n}\n\nexport function loadPreference(key: string, defaultValue: T): T {\n let res = localStorage.getItem(key);\n if (res === null) {\n return defaultValue;\n } else {\n try {\n return JSON.parse(res) as T;\n } catch (e) {\n return defaultValue;\n }\n }\n}\n\nexport function getAvailableCharsForTrackTitle(trackTitles: string[]) {\n const maxChars = 1700; // see https://www.minidisc.org/md_toc.html\n const usedChars = trackTitles.reduce((acc, title) => {\n return acc + title.length;\n }, 0);\n return maxChars - usedChars;\n}\n\nexport function framesToSec(frames: number) {\n return frames / 512;\n}\n\nexport function sanitizeTitle(title: string) {\n return title.normalize('NFD').replace(/[^\\x00-\\x7F]/g, '');\n}\n\ndeclare let process: any;\n","import { openNewDevice, NetMDInterface, Disc, listContent, openPairedDevice, Wireformat, MDTrack, download } from 'netmd-js';\nimport { makeGetAsyncPacketIteratorOnWorkerThread } from 'netmd-js/dist/web-encrypt-worker';\nimport { Logger, ConsoleLogger, Level } from 'netmd-js/dist/logger';\nimport { sanitizeTitle, sleep } from '../utils';\n\nconst Worker = require('worker-loader!netmd-js/dist/web-encrypt-worker.js'); // eslint-disable-line import/no-webpack-loader-syntax\n\nexport interface NetMDService {\n pair(): Promise;\n connect(): Promise;\n listContent(): Promise;\n getDeviceName(): Promise;\n finalize(): Promise;\n renameTrack(index: number, newTitle: string): Promise;\n renameDisc(newName: string): Promise;\n deleteTrack(index: number): Promise;\n moveTrack(src: number, dst: number): Promise;\n wipeDisc(): Promise;\n upload(\n title: string,\n data: ArrayBuffer,\n format: Wireformat,\n progressCallback: (progress: { written: number; encrypted: number; total: number }) => void\n ): Promise;\n\n play(): Promise;\n pause(): Promise;\n stop(): Promise;\n next(): Promise;\n prev(): Promise;\n gotoTrack(index: number): Promise;\n getPosition(): Promise;\n}\n\nexport class NetMDUSBService implements NetMDService {\n private netmdInterface?: NetMDInterface;\n private logger?: Logger;\n\n constructor({ debug = false }: { debug: boolean }) {\n if (debug) {\n // Logging a few methods that have been causing issues with some units\n const _fn = (...args: any) => {\n if (args && args[0] && args[0].method) {\n console.log(...args);\n }\n };\n this.logger = {\n debug: _fn,\n info: _fn,\n warn: _fn,\n error: _fn,\n child: () => this.logger!,\n };\n }\n }\n\n async pair() {\n let iface = await openNewDevice(navigator.usb, this.logger);\n if (iface === null) {\n return false;\n }\n this.netmdInterface = iface;\n return true;\n }\n\n async connect() {\n let iface = await openPairedDevice(navigator.usb, this.logger);\n if (iface === null) {\n return false;\n }\n this.netmdInterface = iface;\n return true;\n }\n\n async listContent() {\n return await listContent(this.netmdInterface!);\n }\n\n async getDeviceName() {\n return await this.netmdInterface!.netMd.getDeviceName();\n }\n\n async finalize() {\n await this.netmdInterface!.netMd.finalize();\n }\n\n async renameTrack(index: number, title: string) {\n // Removing non ascii chars... Sorry, I didn't implement char encoding.\n title = sanitizeTitle(title);\n await this.netmdInterface!.setTrackTitle(index, title);\n }\n\n async renameDisc(newName: string) {\n const oldName = await this.netmdInterface!.getDiscTitle();\n let newNameWithGroups = await this.netmdInterface!._getDiscTitle();\n newNameWithGroups = newNameWithGroups.replace(oldName, newName);\n await this.netmdInterface!.cacheTOC();\n await this.netmdInterface!.setDiscTitle(newNameWithGroups);\n await this.netmdInterface!.syncTOC();\n }\n\n async deleteTrack(index: number) {\n await this.netmdInterface!.eraseTrack(index);\n await sleep(100);\n }\n\n async wipeDisc() {\n await this.netmdInterface!.eraseDisc();\n }\n\n async moveTrack(src: number, dst: number) {\n await this.netmdInterface!.moveTrack(src, dst);\n }\n\n async upload(\n title: string,\n data: ArrayBuffer,\n format: Wireformat,\n progressCallback: (progress: { written: number; encrypted: number; total: number }) => void\n ) {\n let total = data.byteLength;\n let written = 0;\n let encrypted = 0;\n function updateProgress() {\n progressCallback({ written, encrypted, total });\n }\n\n let w = new Worker();\n\n let webWorkerAsyncPacketIterator = makeGetAsyncPacketIteratorOnWorkerThread(w, ({ encryptedBytes }) => {\n encrypted = encryptedBytes;\n updateProgress();\n });\n\n // Removing non ascii chars... Sorry, I didn't implement char encoding.\n title = sanitizeTitle(title);\n let mdTrack = new MDTrack(title, format, data, 0x80000, webWorkerAsyncPacketIterator);\n\n await download(this.netmdInterface!, mdTrack, ({ writtenBytes }) => {\n written = writtenBytes;\n updateProgress();\n });\n\n w.terminate();\n }\n\n async play() {\n await this.netmdInterface!.play();\n }\n async pause() {\n await this.netmdInterface!.pause();\n }\n async stop() {\n await this.netmdInterface!.stop();\n }\n async next() {\n await this.netmdInterface!.nextTrack();\n }\n async prev() {\n await this.netmdInterface!.previousTrack();\n }\n async gotoTrack(index: number) {\n await this.netmdInterface!.gotoTrack(index);\n }\n async getPosition() {\n return await this.netmdInterface!.getPosition();\n }\n}\n","import { NetMDService } from './netmd';\nimport { AudioExportService } from './audio-export';\nimport { MediaRecorderService } from './mediarecorder';\n\ninterface ServiceRegistry {\n netmdService?: NetMDService;\n audioExportService?: AudioExportService;\n mediaRecorderService?: MediaRecorderService;\n}\n\nconst ServiceRegistry: ServiceRegistry = {};\n\nexport default ServiceRegistry;\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface LoadingDialogState {\n visible: boolean;\n writtenProgress: number;\n encryptedProgress: number;\n totalProgress: number;\n\n trackTotal: number;\n trackConverting: number;\n trackCurrent: number;\n\n titleCurrent: string;\n titleConverting: string;\n}\n\nconst initialState: LoadingDialogState = {\n visible: false,\n // Current Track Upload\n writtenProgress: 0,\n encryptedProgress: 0,\n totalProgress: 1,\n\n // Tracks done\n trackTotal: 1,\n trackConverting: 0,\n trackCurrent: 0,\n titleCurrent: '',\n titleConverting: '',\n};\n\nexport const slice = createSlice({\n name: 'uploadDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction) => {\n state.visible = action.payload;\n },\n setWriteProgress: (state, action: PayloadAction<{ written: number; encrypted: number; total: number }>) => {\n state.encryptedProgress = action.payload.encrypted;\n state.writtenProgress = action.payload.written;\n state.totalProgress = action.payload.total;\n },\n setTrackProgress: (\n state,\n action: PayloadAction<{ total: number; current: number; converting: number; titleCurrent: string; titleConverting: string }>\n ) => {\n state.trackTotal = action.payload.total;\n state.trackCurrent = action.payload.current;\n state.trackConverting = action.payload.converting;\n state.titleCurrent = action.payload.titleCurrent;\n state.titleConverting = action.payload.titleConverting;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface RenameDialogState {\n visible: boolean;\n title: string;\n index: number;\n}\n\nconst initialState: RenameDialogState = {\n visible: false,\n title: '',\n index: -1,\n};\n\nexport const slice = createSlice({\n name: 'renameDialog',\n initialState,\n reducers: {\n setVisible: (state: RenameDialogState, action: PayloadAction) => {\n state.visible = action.payload;\n },\n setCurrentName: (state: RenameDialogState, action: PayloadAction) => {\n state.title = action.payload;\n },\n setIndex: (state: RenameDialogState, action: PayloadAction) => {\n state.index = action.payload;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface ErrorDialogState {\n visible: boolean;\n error: string;\n}\n\nconst initialState: ErrorDialogState = {\n visible: false,\n error: ``,\n};\n\nconst slice = createSlice({\n name: 'errorDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction) => {\n state.visible = action.payload;\n },\n setErrorMessage: (state, action: PayloadAction) => {\n state.error = `${action.payload}`;\n },\n },\n});\n\nexport const { actions, reducer } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface ConvertDialogFeature {\n visible: boolean;\n format: string;\n}\n\nconst initialState: ConvertDialogFeature = {\n visible: false,\n format: `LP2`,\n};\n\nconst slice = createSlice({\n name: 'convertDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction) => {\n state.visible = action.payload;\n },\n setFormat: (state, action: PayloadAction) => {\n state.format = action.payload;\n },\n },\n});\n\nexport const { actions, reducer } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface DumpDialogState {\n visible: boolean;\n inputDeviceId: string;\n}\n\nconst initialState: DumpDialogState = {\n visible: false,\n inputDeviceId: '',\n};\n\nexport const slice = createSlice({\n name: 'dumpDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction) => {\n state.visible = action.payload;\n },\n setInputDeviceId: (state, action: PayloadAction) => {\n state.inputDeviceId = action.payload;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface RecordingDialogState {\n visible: boolean;\n\n trackTotal: number;\n trackDone: number;\n trackCurrent: number;\n\n titleCurrent: string;\n}\n\nconst initialState: RecordingDialogState = {\n visible: false,\n\n trackTotal: 1,\n trackDone: 0,\n trackCurrent: 0,\n\n titleCurrent: '',\n\n // visible: true,\n // trackTotal: 4,\n // trackDone: 1,\n // trackCurrent: 25,\n\n // titleCurrent: 'Seconda traccia',\n};\n\nexport const slice = createSlice({\n name: 'recordDialog',\n initialState,\n reducers: {\n setVisible: (state, action: PayloadAction) => {\n state.visible = action.payload;\n },\n setProgress: (\n state,\n action: PayloadAction<{ trackTotal: number; trackDone: number; trackCurrent: number; titleCurrent: string }>\n ) => {\n state.trackTotal = action.payload.trackTotal;\n state.trackDone = action.payload.trackDone;\n state.trackCurrent = action.payload.trackCurrent;\n state.titleCurrent = action.payload.titleCurrent;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\nimport { savePreference, loadPreference } from '../utils';\n\ntype Views = 'WELCOME' | 'MAIN';\n\nexport interface AppState {\n mainView: Views;\n loading: boolean;\n pairingFailed: boolean;\n pairingMessage: string;\n browserSupported: boolean;\n darkMode: boolean;\n aboutDialogVisible: boolean;\n}\n\nconst initialState: AppState = {\n mainView: 'WELCOME',\n loading: false,\n pairingFailed: false,\n pairingMessage: ``,\n browserSupported: true,\n darkMode: loadPreference('darkMode', false),\n aboutDialogVisible: false,\n};\n\nexport const slice = createSlice({\n name: 'app',\n initialState,\n reducers: {\n setState: (state, action: PayloadAction) => {\n state.mainView = action.payload;\n },\n setLoading: (state, action: PayloadAction) => {\n state.loading = action.payload;\n },\n setPairingFailed: (state, action: PayloadAction) => {\n state.pairingFailed = action.payload;\n },\n setPairingMessage: (state, action: PayloadAction) => {\n state.pairingMessage = action.payload;\n },\n setBrowserSupported: (state, action: PayloadAction) => {\n state.browserSupported = action.payload;\n },\n setDarkMode: (state, action: PayloadAction) => {\n state.darkMode = action.payload;\n savePreference('darkMode', state.darkMode);\n },\n showAboutDialog: (state, action: PayloadAction) => {\n state.aboutDialogVisible = action.payload;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { Disc } from 'netmd-js';\nimport { createSlice, PayloadAction } from '@reduxjs/toolkit';\nimport { enableBatching } from 'redux-batched-actions';\n\nexport interface MainState {\n disc: Disc | null;\n deviceName: string;\n}\n\nconst initialState: MainState = {\n disc: null,\n deviceName: '',\n};\n\nexport const slice = createSlice({\n name: 'main',\n initialState,\n reducers: {\n setDisc: (state, action: PayloadAction) => {\n state.disc = action.payload;\n },\n setDeviceName: (state, action: PayloadAction) => {\n state.deviceName = action.payload;\n },\n },\n});\n\nexport const { reducer, actions } = slice;\nexport default enableBatching(reducer);\n","import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';\nimport uploadDialog from './upload-dialog-feature';\nimport renameDialog from './rename-dialog-feature';\nimport errorDialog from './error-dialog-feature';\nimport convertDialog from './convert-dialog-feature';\nimport dumpDialog from './dump-dialog-feature';\nimport recordDialog from './record-dialog-feature';\nimport appState from './app-feature';\nimport main from './main-feature';\n\nexport const store = configureStore({\n reducer: {\n renameDialog,\n uploadDialog,\n errorDialog,\n convertDialog,\n dumpDialog,\n recordDialog,\n appState,\n main,\n },\n middleware: [...getDefaultMiddleware()],\n});\n\nexport type RootState = ReturnType;\nexport type AppDispatch = typeof store.dispatch;\n","import { batchActions } from 'redux-batched-actions';\nimport { AppDispatch, RootState } from './store';\nimport { actions as uploadDialogActions } from './upload-dialog-feature';\nimport { actions as renameDialogActions } from './rename-dialog-feature';\nimport { actions as errorDialogAction } from './error-dialog-feature';\nimport { actions as recordDialogAction } from './record-dialog-feature';\nimport { actions as appStateActions } from './app-feature';\nimport { actions as mainActions } from './main-feature';\nimport serviceRegistry from '../services/registry';\nimport { Wireformat, getTracks } from 'netmd-js';\nimport { AnyAction } from '@reduxjs/toolkit';\nimport { getAvailableCharsForTrackTitle, framesToSec, sleepWithProgressCallback, sleep } from '../utils';\n\nexport function pair() {\n return async function(dispatch: AppDispatch, getState: () => RootState) {\n dispatch(appStateActions.setPairingFailed(false));\n\n await serviceRegistry.audioExportService!.init();\n\n try {\n let connected = await serviceRegistry.netmdService!.connect();\n if (connected) {\n dispatch(appStateActions.setState('MAIN'));\n return;\n }\n } catch (err) {\n console.error(err);\n // In case of error, just log and try to pair\n }\n\n try {\n let paired = await serviceRegistry.netmdService!.pair();\n if (paired) {\n dispatch(appStateActions.setState('MAIN'));\n return;\n }\n dispatch(batchActions([appStateActions.setPairingMessage(`Connection Failed`), appStateActions.setPairingFailed(true)]));\n } catch (err) {\n console.error(err);\n let message = (err as Error).message;\n dispatch(batchActions([appStateActions.setPairingMessage(message), appStateActions.setPairingFailed(true)]));\n }\n };\n}\n\nexport function listContent() {\n return async function(dispatch: AppDispatch) {\n // Issue loading\n dispatch(appStateActions.setLoading(true));\n let disc = await serviceRegistry.netmdService!.listContent();\n let deviceName = await serviceRegistry.netmdService!.getDeviceName();\n dispatch(batchActions([mainActions.setDisc(disc), mainActions.setDeviceName(deviceName), appStateActions.setLoading(false)]));\n };\n}\n\nexport function renameTrack({ index, newName }: { index: number; newName: string }) {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n dispatch(renameDialogActions.setVisible(false));\n try {\n await netmdService!.renameTrack(index, newName);\n } catch (err) {\n console.error(err);\n dispatch(batchActions([errorDialogAction.setVisible(true), errorDialogAction.setErrorMessage(`Rename failed.`)]));\n }\n listContent()(dispatch);\n };\n}\n\nexport function renameDisc({ newName }: { newName: string }) {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n await netmdService!.renameDisc(newName);\n dispatch(renameDialogActions.setVisible(false));\n listContent()(dispatch);\n };\n}\n\nexport function deleteTracks(indexes: number[]) {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n dispatch(appStateActions.setLoading(true));\n indexes = indexes.sort();\n indexes.reverse();\n for (let index of indexes) {\n await netmdService!.deleteTrack(index);\n }\n listContent()(dispatch);\n };\n}\n\nexport function wipeDisc() {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n dispatch(appStateActions.setLoading(true));\n await netmdService!.wipeDisc();\n listContent()(dispatch);\n };\n}\n\nexport function moveTrack(srcIndex: number, destIndex: number) {\n return async function(dispatch: AppDispatch) {\n const { netmdService } = serviceRegistry;\n await netmdService!.moveTrack(srcIndex, destIndex);\n listContent()(dispatch);\n };\n}\n\nexport function recordTracks(indexes: number[], deviceId: string) {\n return async function(dispatch: AppDispatch, getState: () => RootState) {\n dispatch(\n batchActions([\n recordDialogAction.setVisible(true),\n recordDialogAction.setProgress({ trackTotal: indexes.length, trackDone: 0, trackCurrent: 0, titleCurrent: '' }),\n ])\n );\n\n let disc = getState().main.disc;\n let tracks = getTracks(disc!).filter(t => indexes.indexOf(t.index) >= 0);\n\n const { netmdService, mediaRecorderService } = serviceRegistry;\n\n for (let [i, track] of tracks.entries()) {\n dispatch(\n recordDialogAction.setProgress({\n trackTotal: tracks.length,\n trackDone: i,\n trackCurrent: -1,\n titleCurrent: track.title ?? '',\n })\n );\n\n // Wait for the track to be ready to play from 0:00\n await netmdService!.gotoTrack(track.index);\n await netmdService!.play();\n console.log('Waiting for track to be ready to play');\n let position = await netmdService!.getPosition();\n let expected = [track.index, 0, 0, 1];\n while (position === null || !expected.every((_, i) => expected[i] === position![i])) {\n await sleep(250);\n position = await netmdService!.getPosition();\n }\n await netmdService!.pause();\n await netmdService?.gotoTrack(track.index);\n console.log('Track is ready to play');\n\n // Start recording and play track\n await mediaRecorderService?.initStream(deviceId);\n await mediaRecorderService?.startRecording();\n await netmdService!.play();\n\n // Wait until track is finished\n let durationInSec = framesToSec(track.duration);\n // await sleep(durationInSec * 1000);\n await sleepWithProgressCallback(durationInSec * 1000, (perc: number) => {\n dispatch(\n recordDialogAction.setProgress({\n trackTotal: tracks.length,\n trackDone: i,\n trackCurrent: perc,\n titleCurrent: track.title ?? '',\n })\n );\n });\n\n // Stop recording and download the wav\n await mediaRecorderService?.stopRecording();\n mediaRecorderService?.downloadRecorded(`${track.title}`);\n\n await mediaRecorderService?.closeStream();\n }\n\n await netmdService!.stop();\n dispatch(recordDialogAction.setVisible(false));\n };\n}\n\nexport const WireformatDict: { [k: string]: Wireformat } = {\n SP: Wireformat.pcm,\n LP2: Wireformat.lp2,\n LP105: Wireformat.l105kbps,\n LP4: Wireformat.lp4,\n};\n\nexport function convertAndUpload(files: File[], format: string) {\n return async function(dispatch: AppDispatch, getState: () => RootState) {\n const { audioExportService, netmdService } = serviceRegistry;\n const wireformat = WireformatDict[format];\n\n dispatch(uploadDialogActions.setVisible(true));\n\n const updateProgressCallback = ({ written, encrypted, total }: { written: number; encrypted: number; total: number }) => {\n dispatch(uploadDialogActions.setWriteProgress({ written, encrypted, total }));\n };\n\n let trackUpdate: {\n current: number;\n converting: number;\n total: number;\n titleCurrent: string;\n titleConverting: string;\n } = {\n current: 0,\n converting: 0,\n total: files.length,\n titleCurrent: '',\n titleConverting: '',\n };\n const updateTrack = () => {\n dispatch(uploadDialogActions.setTrackProgress(trackUpdate));\n };\n\n let conversionIterator = async function*(files: File[]) {\n let converted: Promise<{ file: File; data: ArrayBuffer }>[] = [];\n\n let i = 0;\n function convertNext() {\n if (i === files.length) {\n trackUpdate.converting = i;\n trackUpdate.titleConverting = ``;\n updateTrack();\n return;\n }\n\n let f = files[i];\n trackUpdate.converting = i;\n trackUpdate.titleConverting = f.name;\n updateTrack();\n i++;\n\n converted.push(\n new Promise(async (resolve, reject) => {\n let data: ArrayBuffer;\n try {\n await audioExportService!.prepare(f);\n data = await audioExportService!.export({ format });\n convertNext();\n resolve({ file: f, data: data });\n } catch (err) {\n error = err;\n errorMessage = `${f.name}: Unsupported or unrecognized format`;\n reject(err);\n }\n })\n );\n }\n convertNext();\n\n let j = 0;\n while (j < converted.length) {\n yield await converted[j];\n delete converted[j];\n j++;\n }\n };\n\n let disc = getState().main.disc;\n let maxTitleLength = disc ? getAvailableCharsForTrackTitle(getTracks(disc).map(track => track.title || ``)) : -1;\n maxTitleLength = Math.floor(maxTitleLength / files.length);\n\n let error: any;\n let errorMessage = ``;\n let i = 1;\n for await (let item of conversionIterator(files)) {\n const { file, data } = item;\n\n let title = file.name;\n const extStartIndex = title.lastIndexOf('.');\n if (extStartIndex > 0) {\n title = title.substring(0, extStartIndex);\n }\n if (maxTitleLength > -1) {\n title = title.substring(0, maxTitleLength);\n }\n\n trackUpdate.current = i++;\n trackUpdate.titleCurrent = title;\n updateTrack();\n updateProgressCallback({ written: 0, encrypted: 0, total: 100 });\n try {\n await netmdService?.upload(title, data, wireformat, updateProgressCallback);\n } catch (err) {\n error = err;\n errorMessage = `${file.name}: Error uploading to device`;\n break;\n }\n }\n\n let actionToDispatch: AnyAction[] = [uploadDialogActions.setVisible(false)];\n\n if (error) {\n console.error(error);\n actionToDispatch = actionToDispatch.concat([\n errorDialogAction.setVisible(true),\n errorDialogAction.setErrorMessage(errorMessage),\n ]);\n }\n\n dispatch(batchActions(actionToDispatch));\n listContent()(dispatch);\n };\n}\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\n\nimport { actions as appActions } from '../redux/app-feature';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogContentText from '@material-ui/core/DialogContentText';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\nimport Link from '@material-ui/core/Link';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return ;\n});\n\nexport const AboutDialog = (props: {}) => {\n const dispatch = useDispatch();\n\n let visible = useShallowEqualSelector(state => state.appState.aboutDialogVisible);\n\n const handleClose = () => {\n dispatch(appActions.showAboutDialog(false));\n };\n\n return (\n \n About Web MiniDisc\n \n Web MiniDisc has been made possible by\n
    \n
  • \n \n FFmpeg\n {' '}\n and{' '}\n \n ffmpegjs\n \n , to read your audio files (wav, mp3, ogg, mp4, etc...).\n
  • \n
  • \n \n Atracdenc\n \n , to support atrac3 encoding (lp2, lp4 audio formats).\n
  • \n
  • \n \n Emscripten\n \n , to run both FFmpeg and Atracdenc in the browser.\n
  • \n
  • \n \n netmd-js\n \n , to send commands to NetMD devices using Javascript.\n
  • \n
  • \n \n linux-minidisc\n \n , to make the netmd-js project possible.\n
  • \n
  • \n \n material-ui\n \n , to build the user interface.\n
  • \n
\n Attribution\n
    \n
  • \n MiniDisc logo from{' '}\n \n https://en.wikipedia.org/wiki/MiniDisc\n \n
  • \n
  • \n MiniDisc icon from{' '}\n \n http://fav.me/d7u3g3g\n \n
  • \n
\n
\n \n \n \n \n );\n};\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { batchActions } from 'redux-batched-actions';\n\nimport IconButton from '@material-ui/core/IconButton';\nimport Menu from '@material-ui/core/Menu';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport MoreVertIcon from '@material-ui/icons/MoreVert';\n\nimport { wipeDisc, listContent } from '../redux/actions';\nimport { actions as appActions } from '../redux/app-feature';\nimport { actions as renameDialogActions } from '../redux/rename-dialog-feature';\nimport { useShallowEqualSelector } from '../utils';\nimport Link from '@material-ui/core/Link';\n\nexport const TopMenu = function() {\n const dispatch = useDispatch();\n\n let { mainView } = useShallowEqualSelector(state => state.appState);\n let disc = useShallowEqualSelector(state => state.main.disc);\n\n const [menuAnchorEl, setMenuAnchorEl] = React.useState(null);\n const menuOpen = Boolean(menuAnchorEl);\n const handleMenuClick = (event: React.MouseEvent) => {\n setMenuAnchorEl(event.currentTarget);\n };\n\n const handleMenuClose = () => {\n setMenuAnchorEl(null);\n };\n\n const handleWipeDisc = () => {\n dispatch(wipeDisc());\n handleMenuClose();\n };\n\n const handleRefresh = () => {\n dispatch(listContent());\n handleMenuClose();\n };\n\n const handleRenameDisc = () => {\n dispatch(\n batchActions([\n renameDialogActions.setVisible(true),\n renameDialogActions.setCurrentName(disc?.title ?? ``),\n renameDialogActions.setIndex(-1),\n ])\n );\n handleMenuClose();\n };\n\n const handleExit = () => {\n dispatch(appActions.setState('WELCOME'));\n handleMenuClose();\n };\n const handleShowAbout = () => {\n dispatch(appActions.showAboutDialog(true));\n handleMenuClose();\n };\n\n const menuItems = [];\n if (mainView === 'MAIN') {\n menuItems.push(\n \n Refresh\n \n );\n menuItems.push(\n \n Rename Disc\n \n );\n menuItems.push(\n \n Wipe Disc\n \n );\n menuItems.push(\n \n Exit\n \n );\n }\n menuItems.push(\n \n About\n \n );\n menuItems.push(\n \n \n Fork me on GitHub\n \n \n );\n\n return (\n \n \n \n \n \n {menuItems}\n \n \n );\n};\n","import React, { useState } from 'react';\nimport { useDispatch } from 'react-redux';\nimport { pair } from '../redux/actions';\n\nimport { useShallowEqualSelector } from '../utils';\n\nimport { makeStyles } from '@material-ui/core/styles';\nimport Button from '@material-ui/core/Button';\nimport Typography from '@material-ui/core/Typography';\nimport FormControl from '@material-ui/core/FormControl';\nimport FormHelperText from '@material-ui/core/FormHelperText';\nimport Box from '@material-ui/core/Box';\nimport Link from '@material-ui/core/Link';\n\nimport { AboutDialog } from './about-dialog';\nimport { TopMenu } from './topmenu';\nimport ChromeIconPath from '../images/chrome-icon.svg';\n\nconst useStyles = makeStyles(theme => ({\n main: {\n position: 'relative',\n flex: '1 1 auto',\n display: 'flex',\n justifyContent: 'center',\n flexDirection: 'column',\n alignItems: 'center',\n },\n button: {\n marginTop: theme.spacing(3),\n minWidth: 150,\n },\n spacing: {\n marginTop: theme.spacing(1),\n },\n chromeLogo: {\n marginTop: theme.spacing(1),\n width: 96,\n height: 96,\n },\n why: {\n alignSelf: 'flex-start',\n marginTop: theme.spacing(3),\n },\n headBox: {\n display: 'flex',\n justifyContent: 'space-between',\n },\n}));\n\nexport const Welcome = (props: {}) => {\n const classes = useStyles();\n\n const dispatch = useDispatch();\n const { browserSupported, pairingFailed, pairingMessage } = useShallowEqualSelector(state => state.appState);\n if (pairingMessage.toLowerCase().match(/denied/)) {\n // show linux instructions\n }\n // Access denied.\n\n const [showWhyUnsupported, setWhyUnsupported] = useState(false);\n const handleLearnWhy = (event: React.SyntheticEvent) => {\n event.preventDefault();\n setWhyUnsupported(true);\n };\n\n return (\n \n \n \n Web MiniDisc\n \n \n \n \n Brings NetMD Devices to the Web\n \n \n {browserSupported ? (\n \n \n Press the button to connect to a NetMD device\n \n\n \n\n \n {pairingMessage}\n \n \n ) : (\n \n \n This Web browser is not supported. \n \n Learn Why\n \n \n\n \n \"Chrome\n \n\n \n Try using{' '}\n \n Chrome\n {' '}\n instead\n \n\n {showWhyUnsupported ? (\n <>\n \n Web MiniDisc requires a browser that supports both{' '}\n \n WebUSB\n {' '}\n and{' '}\n \n WebAssembly\n \n .\n \n
    \n
  • WebUSB is needed to control the NetMD device via the USB connection to your computer.
  • \n
  • WebAssembly is used to convert the music to a MiniDisc compatible format
  • \n
\n \n ) : null}\n
\n )}\n
\n \n
\n );\n};\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\nimport { actions as renameDialogActions } from '../redux/rename-dialog-feature';\nimport { renameTrack, renameDisc } from '../redux/actions';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport TextField from '@material-ui/core/TextField';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return ;\n});\n\nexport const RenameDialog = (props: {}) => {\n let dispatch = useDispatch();\n\n let renameDialogVisible = useShallowEqualSelector(state => state.renameDialog.visible);\n let renameDialogTitle = useShallowEqualSelector(state => state.renameDialog.title);\n let renameDialogIndex = useShallowEqualSelector(state => state.renameDialog.index);\n\n const what = renameDialogIndex < 0 ? `Disc` : `Track`;\n\n const handleCancelRename = () => {\n dispatch(renameDialogActions.setVisible(false));\n };\n\n const handleDoRename = () => {\n if (renameDialogIndex < 0) {\n dispatch(renameDisc({ newName: renameDialogTitle }));\n } else {\n dispatch(renameTrack({ index: renameDialogIndex, newName: renameDialogTitle }));\n }\n };\n\n return (\n \n Rename {what}\n \n {\n event.key === `Enter` && handleDoRename();\n }}\n onChange={event => {\n dispatch(renameDialogActions.setCurrentName(event.target.value.substring(0, 120))); // MAX title length\n }}\n />\n \n \n \n \n \n \n );\n};\n","import React from 'react';\nimport { useShallowEqualSelector } from '../utils';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogContentText from '@material-ui/core/DialogContentText';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport LinearProgress from '@material-ui/core/LinearProgress';\nimport Box from '@material-ui/core/Box';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles(theme => ({\n progressPerc: {\n marginTop: theme.spacing(1),\n },\n progressBar: {\n marginTop: theme.spacing(3),\n },\n uploadLabel: {\n marginTop: theme.spacing(3),\n },\n}));\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return ;\n});\n\nexport const UploadDialog = (props: {}) => {\n const classes = useStyles();\n\n let {\n visible,\n writtenProgress,\n encryptedProgress,\n totalProgress,\n\n trackTotal,\n trackCurrent,\n trackConverting,\n titleCurrent,\n titleConverting,\n } = useShallowEqualSelector(state => state.uploadDialog);\n\n let progressValue = Math.floor((writtenProgress / totalProgress) * 100);\n let bufferValue = Math.floor((encryptedProgress / totalProgress) * 100);\n let convertedValue = Math.floor((trackConverting / trackTotal) * 100);\n return (\n \n Recording...\n \n \n {convertedValue === 100 && trackConverting === trackTotal\n ? `Conversion completed`\n : `Converting ${trackConverting + 1} of ${trackTotal}: ${titleConverting}`}\n \n \n {convertedValue}%\n\n \n Uploading {trackCurrent} of {trackTotal}: {titleCurrent}\n \n \n {progressValue}%\n \n \n \n );\n};\n","import React from 'react';\nimport { useShallowEqualSelector } from '../utils';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogContentText from '@material-ui/core/DialogContentText';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport LinearProgress from '@material-ui/core/LinearProgress';\nimport Box from '@material-ui/core/Box';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles(theme => ({\n progressPerc: {\n marginTop: theme.spacing(1),\n },\n progressBar: {\n marginTop: theme.spacing(3),\n },\n}));\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return ;\n});\n\nexport const RecordDialog = (props: {}) => {\n const classes = useStyles();\n\n let { visible, trackTotal, trackDone, trackCurrent, titleCurrent } = useShallowEqualSelector(state => state.recordDialog);\n\n let progressValue = Math.round(trackCurrent);\n return (\n \n Recording...\n \n \n {`Recording track ${trackDone + 1} of ${trackTotal}: ${titleCurrent}`}\n \n = 0 ? 'determinate' : 'indeterminate'}\n color=\"primary\"\n value={progressValue}\n />\n {progressValue >= 0 ? `${progressValue}%` : ``}\n \n \n \n );\n};\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\n\nimport { actions as errorDialogActions } from '../redux/error-dialog-feature';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogContentText from '@material-ui/core/DialogContentText';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return ;\n});\n\nexport const ErrorDialog = (props: {}) => {\n const dispatch = useDispatch();\n\n let { visible, error } = useShallowEqualSelector(state => state.errorDialog);\n\n const handleClose = () => {\n dispatch(errorDialogActions.setVisible(false));\n };\n\n return (\n \n Error\n \n {error}\n \n \n \n \n \n );\n};\n","import React from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\n\nimport { actions as convertDialogActions } from '../redux/convert-dialog-feature';\nimport { convertAndUpload } from '../redux/actions';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\nimport { makeStyles } from '@material-ui/core/styles';\nimport FormControl from '@material-ui/core/FormControl';\nimport InputLabel from '@material-ui/core/InputLabel';\nimport Select from '@material-ui/core/Select';\nimport Input from '@material-ui/core/Input';\nimport MenuItem from '@material-ui/core/MenuItem';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return ;\n});\n\nconst useStyles = makeStyles(theme => ({\n container: {\n display: 'flex',\n flexDirection: 'row',\n },\n formControl: {\n minWidth: 120,\n },\n}));\n\nexport const ConvertDialog = (props: { files: File[] }) => {\n const dispatch = useDispatch();\n const classes = useStyles();\n\n let { visible, format } = useShallowEqualSelector(state => state.convertDialog);\n\n const handleClose = () => {\n dispatch(convertDialogActions.setVisible(false));\n };\n\n const handleChange = (ev: React.ChangeEvent<{ value: unknown }>) => {\n dispatch(convertDialogActions.setFormat(ev.target.value as string));\n };\n\n const handleConvert = () => {\n handleClose();\n dispatch(convertAndUpload(props.files, format));\n };\n\n return (\n \n Upload Settings\n \n \n \n Format\n \n }\n >\n SP\n LP2\n LP4\n \n \n \n \n \n \n \n \n );\n};\n","import React, { useCallback } from 'react';\n\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport StopIcon from '@material-ui/icons/Stop';\nimport SkipNextIcon from '@material-ui/icons/SkipNext';\nimport SkipPreviousIcon from '@material-ui/icons/SkipPrevious';\n\nimport IconButton from '@material-ui/core/IconButton';\nimport Box from '@material-ui/core/Box';\n\nimport serviceRegistry from '../services/registry';\n\nexport const Controls = () => {\n const handlePrev = useCallback(() => {\n serviceRegistry.netmdService?.prev();\n }, []);\n const handlePlay = useCallback(() => {\n serviceRegistry.netmdService?.play();\n }, []);\n const handleStop = useCallback(() => {\n serviceRegistry.netmdService?.stop();\n }, []);\n const handleNext = useCallback(() => {\n serviceRegistry.netmdService?.next();\n }, []);\n return (\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n );\n};\n","import React, { useEffect, useState, useCallback } from 'react';\nimport { useDispatch } from 'react-redux';\nimport { useShallowEqualSelector } from '../utils';\n\nimport { recordTracks } from '../redux/actions';\nimport { actions as dumpDialogActions } from '../redux/dump-dialog-feature';\n\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport Slide from '@material-ui/core/Slide';\nimport Button from '@material-ui/core/Button';\nimport { makeStyles } from '@material-ui/core/styles';\nimport FormControl from '@material-ui/core/FormControl';\nimport Select from '@material-ui/core/Select';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport Typography from '@material-ui/core/Typography';\nimport FormHelperText from '@material-ui/core/FormHelperText';\nimport { Controls } from './controls';\nimport Box from '@material-ui/core/Box';\nimport serviceRegistry from '../services/registry';\n\nconst Transition = React.forwardRef(function Transition(props, ref) {\n return ;\n});\n\nconst useStyles = makeStyles(theme => ({\n container: {\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'flex-end',\n justifyContent: 'space-between',\n marginRight: -theme.spacing(2),\n },\n formControl: {\n minWidth: 120,\n },\n selectEmpty: {\n marginTop: theme.spacing(2),\n },\n head: {\n textShadow: '0px 0px 12px rgba(150, 150, 150, 1)',\n fontSize: theme.typography.h2.fontSize,\n textAlign: 'center',\n marginBottom: theme.spacing(2),\n },\n}));\n\nexport const DumpDialog = ({ trackIndexes }: { trackIndexes: number[] }) => {\n const dispatch = useDispatch();\n const classes = useStyles();\n\n const [devices, setDevices] = useState<{ deviceId: string; label: string }[]>([]);\n const [inputDeviceId, setInputDeviceId] = useState('');\n\n let { visible } = useShallowEqualSelector(state => state.dumpDialog);\n\n const handleClose = useCallback(() => {\n setInputDeviceId('');\n serviceRegistry.mediaRecorderService?.stopTestInput();\n dispatch(dumpDialogActions.setVisible(false));\n }, [dispatch]);\n\n const handleChange = useCallback(\n (ev: React.ChangeEvent<{ value: unknown }>) => {\n const deviceId = ev.target.value as string;\n setInputDeviceId(deviceId);\n serviceRegistry.mediaRecorderService?.stopTestInput();\n serviceRegistry.mediaRecorderService?.playTestInput(deviceId);\n },\n [setInputDeviceId]\n );\n\n const handleStartTransfer = useCallback(() => {\n dispatch(recordTracks(trackIndexes, inputDeviceId));\n handleClose();\n }, [trackIndexes, inputDeviceId, dispatch, handleClose]);\n\n useEffect(() => {\n async function updateDeviceList() {\n await navigator.mediaDevices.getUserMedia({ audio: true });\n let devices = await navigator.mediaDevices.enumerateDevices();\n let inputDevices = devices\n .filter(device => device.kind === 'audioinput')\n .map(device => ({ deviceId: device.deviceId, label: device.label }));\n setDevices(inputDevices);\n }\n if (visible) {\n updateDeviceList();\n }\n }, [visible, setDevices]);\n\n return (\n \n Record Selected Tracks\n \n \n {`💻 ⬅ 💽`}\n \n \n 1. Connect your MD Player line-out to your PC audio line-in.\n \n \n 2. Use the controls at the bottom right to play some tracks.\n \n \n 3. Select the input source. You should hear the tracks playing on your PC.\n \n \n 4. Adjust the input gain and the line-out volume of your device.\n \n \n \n \n Input Source\n \n \n \n \n \n \n \n \n \n );\n};\n","import React, { useEffect, useCallback } from 'react';\nimport { useDispatch } from 'react-redux';\nimport clsx from 'clsx';\nimport { useDropzone } from 'react-dropzone';\nimport { listContent, deleteTracks, moveTrack } from '../redux/actions';\nimport { actions as renameDialogActions } from '../redux/rename-dialog-feature';\nimport { actions as convertDialogActions } from '../redux/convert-dialog-feature';\nimport { actions as dumpDialogActions } from '../redux/dump-dialog-feature';\n\nimport { formatTimeFromFrames, getTracks, Encoding } from 'netmd-js';\n\nimport { useShallowEqualSelector } from '../utils';\n\nimport { lighten, makeStyles } from '@material-ui/core/styles';\nimport Typography from '@material-ui/core/Typography';\nimport Box from '@material-ui/core/Box';\nimport Fab from '@material-ui/core/Fab';\nimport AddIcon from '@material-ui/icons/Add';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport EditIcon from '@material-ui/icons/Edit';\nimport Backdrop from '@material-ui/core/Backdrop';\n\nimport Table from '@material-ui/core/Table';\nimport TableBody from '@material-ui/core/TableBody';\nimport TableCell from '@material-ui/core/TableCell';\nimport TableHead from '@material-ui/core/TableHead';\nimport TableRow from '@material-ui/core/TableRow';\n\nimport IconButton from '@material-ui/core/IconButton';\nimport Toolbar from '@material-ui/core/Toolbar';\nimport Tooltip from '@material-ui/core/Tooltip';\nimport { batchActions } from 'redux-batched-actions';\n\nimport { RenameDialog } from './rename-dialog';\nimport { UploadDialog } from './upload-dialog';\nimport { RecordDialog } from './record-dialog';\nimport { ErrorDialog } from './error-dialog';\nimport { ConvertDialog } from './convert-dialog';\nimport { AboutDialog } from './about-dialog';\nimport { DumpDialog } from './dump-dialog';\nimport { TopMenu } from './topmenu';\nimport Checkbox from '@material-ui/core/Checkbox';\nimport * as BadgeImpl from '@material-ui/core/Badge/Badge';\nimport Button from '@material-ui/core/Button';\nimport Menu from '@material-ui/core/Menu';\nimport MenuItem from '@material-ui/core/MenuItem';\n\nconst useStyles = makeStyles(theme => ({\n add: {\n position: 'absolute',\n bottom: theme.spacing(3),\n right: theme.spacing(3),\n },\n main: {\n overflowY: 'auto',\n flex: '1 1 auto',\n marginBottom: theme.spacing(3),\n marginLeft: theme.spacing(-2),\n marginRight: theme.spacing(-2),\n outline: 'none',\n },\n toolbar: {\n marginTop: theme.spacing(3),\n marginLeft: theme.spacing(-2),\n marginRight: theme.spacing(-2),\n [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {\n marginLeft: theme.spacing(-3),\n marginRight: theme.spacing(-3),\n },\n },\n toolbarLabel: {\n flex: '1 1 100%',\n },\n toolbarHighlight:\n theme.palette.type === 'light'\n ? {\n color: theme.palette.secondary.main,\n backgroundColor: lighten(theme.palette.secondary.light, 0.85),\n }\n : {\n color: theme.palette.text.primary,\n backgroundColor: theme.palette.secondary.dark,\n },\n headBox: {\n display: 'flex',\n justifyContent: 'space-between',\n },\n spacing: {\n marginTop: theme.spacing(1),\n },\n formatBadge: {\n ...(BadgeImpl as any).styles(theme).badge,\n ...(BadgeImpl as any).styles(theme).colorPrimary,\n position: 'static',\n display: 'inline-flex',\n border: `2px solid ${theme.palette.background.paper}`,\n padding: '0 4px',\n },\n titleCell: {\n overflow: 'hidden',\n maxWidth: '40ch',\n textOverflow: 'ellipsis',\n // whiteSpace: 'nowrap',\n },\n indexCell: {\n whiteSpace: 'nowrap',\n paddingRight: 0,\n width: `2ch`,\n },\n backdrop: {\n zIndex: theme.zIndex.drawer + 1,\n color: '#fff',\n },\n}));\n\nconst EncodingName: { [k: number]: string } = {\n [Encoding.sp]: 'SP',\n [Encoding.lp2]: 'LP2',\n [Encoding.lp4]: 'LP4',\n};\n\nexport const Main = (props: {}) => {\n let dispatch = useDispatch();\n let disc = useShallowEqualSelector(state => state.main.disc);\n let deviceName = useShallowEqualSelector(state => state.main.deviceName);\n\n const [selected, setSelected] = React.useState([]);\n const selectedCount = selected.length;\n\n const [moveMenuAnchorEl, setMoveMenuAnchorEl] = React.useState(null);\n const handleShowMoveMenu = useCallback((event: React.MouseEvent) => {\n setMoveMenuAnchorEl(event.currentTarget);\n }, []);\n const handleCloseMoveMenu = useCallback(() => {\n setMoveMenuAnchorEl(null);\n }, []);\n const handleMoveSelectedTrack = useCallback(\n (destIndex: number) => {\n dispatch(moveTrack(selected[0], destIndex));\n handleCloseMoveMenu();\n },\n [dispatch, selected, handleCloseMoveMenu]\n );\n\n const handleShowDumpDialog = useCallback(() => {\n dispatch(dumpDialogActions.setVisible(true));\n }, [dispatch]);\n\n useEffect(() => {\n dispatch(listContent());\n }, [dispatch]);\n\n useEffect(() => {\n setSelected([]); // Reset selection if disc changes\n }, [disc]);\n\n let [uploadedFiles, setUploadedFiles] = React.useState([]);\n const onDrop = useCallback(\n (acceptedFiles: File[], rejectedFiles: File[]) => {\n setUploadedFiles(acceptedFiles);\n dispatch(convertDialogActions.setVisible(true));\n },\n [dispatch]\n );\n const { getRootProps, getInputProps, isDragActive, open } = useDropzone({ onDrop, accept: `audio/*`, noClick: true });\n\n const classes = useStyles();\n\n let tracks: { index: number; title: string; group: string; duration: string; encoding: string }[] = [];\n if (disc !== null) {\n for (let group of disc.groups) {\n for (let track of group.tracks) {\n tracks.push({\n index: track.index,\n title: track.title ?? `Unknown Title`,\n group: group.title ?? ``,\n encoding: EncodingName[track.encoding],\n duration: formatTimeFromFrames(track.duration, false),\n });\n }\n }\n }\n tracks.sort((l, r) => l.index - r.index);\n\n // Action Handlers\n const handleSelectClick = (event: React.MouseEvent, item: number) => {\n if (selected.includes(item)) {\n setSelected(selected.filter(i => i !== item));\n } else {\n setSelected([...selected, item]);\n }\n };\n\n const handleSelectAllClick = (event: React.ChangeEvent) => {\n if (selected.length < tracks.length) {\n setSelected(tracks.map(t => t.index));\n } else {\n setSelected([]);\n }\n };\n\n const handleRenameDoubleClick = (event: React.MouseEvent, item: number) => {\n let selectedIndex = item;\n let currentName = getTracks(disc!).find(track => track.index === selectedIndex)?.title ?? '';\n\n dispatch(\n batchActions([\n renameDialogActions.setVisible(true),\n renameDialogActions.setCurrentName(currentName),\n renameDialogActions.setIndex(selectedIndex),\n ])\n );\n };\n\n const handleRenameActionClick = (event: React.MouseEvent) => {\n handleRenameDoubleClick(event, selected[0]);\n };\n\n const handleDeleteSelected = (event: React.MouseEvent) => {\n dispatch(deleteTracks(selected));\n };\n\n return (\n \n \n \n {deviceName || `Loading...`}\n \n \n \n \n {disc !== null\n ? `${formatTimeFromFrames(disc.left, false)} left of ${formatTimeFromFrames(disc.total, false)}`\n : `Loading...`}\n \n 0,\n })}\n >\n {selectedCount > 0 ? (\n 0 && selectedCount < tracks.length}\n checked={selectedCount > 0}\n onChange={handleSelectAllClick}\n inputProps={{ 'aria-label': 'select all tracks' }}\n />\n ) : null}\n {selectedCount > 0 ? (\n \n {selectedCount} selected\n \n ) : (\n \n {disc?.title || `Untitled Disc`}\n \n )}\n {selectedCount === 1 ? (\n \n \n \n \n \n {Array(tracks.length)\n .fill(null)\n .map((_, i) => {\n return (\n handleMoveSelectedTrack(i)}>\n {i + 1}\n \n );\n })}\n \n \n ) : null}\n\n {selectedCount > 0 ? (\n \n \n \n \n \n ) : null}\n\n {selectedCount > 0 ? (\n \n \n \n \n \n ) : null}\n\n {selectedCount > 0 ? (\n \n \n \n \n \n ) : null}\n \n \n \n \n \n \n #\n Title\n Format\n Duration\n \n \n \n {tracks.map(track => (\n handleRenameDoubleClick(event, track.index)}\n onClick={event => handleSelectClick(event, track.index)}\n >\n {track.index + 1}\n \n {track.title || `No Title`}\n \n \n {track.encoding}\n \n {track.duration}\n \n ))}\n \n
\n \n Drop your Music to Upload\n \n
\n \n \n \n\n \n \n \n \n \n \n \n
\n );\n};\n","/* eslint no-restricted-globals: 0 */\nimport { getPublicPathFor } from '../utils';\nexport class AtracdencProcess {\n private messageCallback?: (ev: MessageEvent) => void;\n\n constructor(public worker: Worker) {\n worker.onmessage = this.handleMessage.bind(this);\n }\n\n async init() {\n await new Promise(resolve => {\n this.messageCallback = resolve;\n this.worker.postMessage({ action: 'init' });\n });\n }\n\n async encode(data: ArrayBuffer, bitrate: string) {\n let eventData = await new Promise(resolve => {\n this.messageCallback = resolve;\n this.worker.postMessage({ action: 'encode', bitrate, data }, [data]);\n });\n return eventData.data.result as Uint8Array;\n }\n\n terminate() {\n this.worker.terminate();\n }\n\n handleMessage(ev: MessageEvent) {\n this.messageCallback!(ev);\n this.messageCallback = undefined;\n }\n}\n\nif (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {\n // Worker\n let Module: any;\n onmessage = async (ev: MessageEvent) => {\n const { action, ...others } = ev.data;\n if (action === 'init') {\n self.importScripts(getPublicPathFor(`atracdenc.js`));\n (self as any).Module().then((m: any) => {\n Module = m;\n self.postMessage({ action: 'init' });\n Module.setLogger && Module.setLogger((msg: string, stream: string) => console.log(`${stream}: ${msg}`));\n });\n } else if (action === 'encode') {\n const { bitrate, data } = others;\n const inWavFile = `inWavFile.wav`;\n const outAt3File = `outAt3File.aea`;\n const dataArray = new Uint8Array(data);\n Module.FS.writeFile(`${inWavFile}`, dataArray);\n Module.callMain([`-e`, `atrac3`, `-i`, inWavFile, `-o`, outAt3File, `--bitrate`, bitrate]);\n\n // Read file and trim header (96 bytes)\n let fileStat = Module.FS.stat(outAt3File);\n let size = fileStat.size;\n let tmp = new Uint8Array(size - 96);\n let outAt3FileStream = Module.FS.open(outAt3File, 'r');\n Module.FS.read(outAt3FileStream, tmp, 0, tmp.length, 96);\n Module.FS.close(outAt3FileStream);\n\n let result = tmp.buffer;\n\n self.postMessage(\n {\n action: 'encode',\n result,\n },\n [result]\n );\n }\n };\n} else {\n // Main\n}\n","import React from 'react';\nimport { useShallowEqualSelector } from '../utils';\nimport { actions as appActions } from '../redux/app-feature';\n\nimport CssBaseline from '@material-ui/core/CssBaseline';\nimport Backdrop from '@material-ui/core/Backdrop';\nimport CircularProgress from '@material-ui/core/CircularProgress';\nimport { makeStyles, createMuiTheme, ThemeProvider } from '@material-ui/core/styles';\n\nimport { Welcome } from './welcome';\nimport { Main } from './main';\nimport { Controls } from './controls';\nimport Paper from '@material-ui/core/Paper';\nimport Typography from '@material-ui/core/Typography';\nimport Link from '@material-ui/core/Link';\nimport Box from '@material-ui/core/Box';\nimport Brightness2Icon from '@material-ui/icons/Brightness2';\nimport IconButton from '@material-ui/core/IconButton';\nimport { useDispatch } from 'react-redux';\n\nconst useStyles = makeStyles(theme => ({\n layout: {\n width: 'auto',\n height: '100%',\n [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {\n width: 600,\n marginLeft: 'auto',\n marginRight: 'auto',\n },\n },\n\n paper: {\n position: 'relative',\n display: 'flex',\n flexDirection: 'column',\n padding: theme.spacing(2),\n height: '100%',\n [theme.breakpoints.up(600 + theme.spacing(2) * 2)]: {\n marginTop: theme.spacing(6),\n marginBottom: theme.spacing(6),\n padding: theme.spacing(3),\n height: 600,\n },\n },\n copyright: {\n display: 'flex',\n alignItems: 'center',\n [theme.breakpoints.down(600 + theme.spacing(2) * 2)]: {\n flexWrap: 'wrap',\n },\n },\n backdrop: {\n zIndex: theme.zIndex.drawer + 1,\n color: '#fff',\n },\n minidiscLogo: {\n width: 48,\n },\n controlsContainer: {\n flex: '1 1 auto',\n paddingLeft: theme.spacing(3),\n paddingRight: theme.spacing(8),\n [theme.breakpoints.down(600 + theme.spacing(2) * 2)]: {\n order: -1,\n width: '100%',\n paddingLeft: 0,\n },\n },\n}));\n\nconst darkTheme = createMuiTheme({\n palette: {\n type: 'dark',\n primary: {\n light: '#6ec6ff',\n main: '#2196f3',\n dark: '#0069c0',\n contrastText: '#fff',\n },\n },\n});\n\nconst lightTheme = createMuiTheme({\n palette: {\n type: 'light',\n },\n});\n\nconst App = () => {\n const classes = useStyles();\n\n const dispatch = useDispatch();\n let { mainView, loading, darkMode } = useShallowEqualSelector(state => state.appState);\n\n return (\n \n \n \n\n
\n \n {mainView === 'WELCOME' ? : null}\n {mainView === 'MAIN' ?
: null}\n\n \n dispatch(appActions.setDarkMode(!darkMode))}>\n \n \n \n {'© '}\n \n Stefano Brilli\n {' '}\n {new Date().getFullYear()}\n {'.'}\n \n \n Tweet\n \n {mainView === 'MAIN' ? : null}\n \n \n
\n\n \n \n \n \n \n );\n};\n\nexport default App;\n","import { createWorker, setLogging } from '@ffmpeg/ffmpeg';\nimport { AtracdencProcess } from './atracdenc-worker';\nimport { getPublicPathFor } from '../utils';\nconst AtracdencWorker = require('worker-loader!./atracdenc-worker'); // eslint-disable-line import/no-webpack-loader-syntax\n\ninterface LogPayload {\n message: string;\n action: string;\n}\n\nexport interface AudioExportService {\n init(): Promise;\n export(params: { format: string }): Promise;\n info(): Promise<{ format: string | null; input: string | null }>;\n prepare(file: File): Promise;\n}\n\nexport class FFMpegAudioExportService implements AudioExportService {\n public ffmpegProcess: any;\n public atracdencProcess?: AtracdencProcess;\n public loglines: { action: string; message: string }[] = [];\n public inFileName: string = ``;\n public outFileNameNoExt: string = ``;\n\n async init() {\n setLogging(true);\n }\n\n async prepare(file: File) {\n this.loglines = [];\n this.ffmpegProcess = createWorker({\n logger: (payload: LogPayload) => {\n this.loglines.push(payload);\n console.log(payload.action, payload.message);\n },\n corePath: getPublicPathFor('ffmpeg-core.js'),\n workerPath: getPublicPathFor('worker.min.js'),\n });\n await this.ffmpegProcess.load();\n\n this.atracdencProcess = new AtracdencProcess(new AtracdencWorker());\n await this.atracdencProcess.init();\n\n let ext = file.name.split('.').slice(-1);\n if (ext.length === 0) {\n throw new Error(`Unrecognized file format: ${file.name}`);\n }\n\n this.inFileName = `inAudioFile.${ext[0]}`;\n this.outFileNameNoExt = `outAudioFile`;\n\n await this.ffmpegProcess.write(this.inFileName, file);\n }\n\n async info() {\n await this.ffmpegProcess.transcode(this.inFileName, `${this.outFileNameNoExt}.metadata`, `-f ffmetadata`);\n\n let audioFormatRegex = /Audio:\\s(.*?),/; // Actual content\n let inputFormatRegex = /Input #0,\\s(.*?),/; // Container\n let format: string | null = null;\n let input: string | null = null;\n\n for (let line of this.loglines) {\n let match = line.message.match(audioFormatRegex);\n if (match !== null) {\n format = match[1];\n continue;\n }\n match = line.message.match(inputFormatRegex);\n if (match !== null) {\n input = match[1];\n continue;\n }\n if (format !== null && input !== null) {\n break;\n }\n }\n\n return { format, input };\n }\n\n async export({ format }: { format: string }) {\n let result: ArrayBuffer;\n if (format === `SP`) {\n const outFileName = `${this.outFileNameNoExt}.raw`;\n await this.ffmpegProcess.transcode(this.inFileName, outFileName, '-f s16be -ar 44100');\n let { data } = await this.ffmpegProcess.read(outFileName);\n result = data.buffer;\n } else {\n const outFileName = `${this.outFileNameNoExt}.wav`;\n await this.ffmpegProcess.transcode(this.inFileName, outFileName, '-f wav -ar 44100');\n let { data } = await this.ffmpegProcess.read(outFileName);\n let bitrate: string = `0`;\n switch (format) {\n case `LP2`:\n bitrate = `128`;\n break;\n case `LP105`:\n bitrate = `102`;\n break;\n case `LP4`:\n bitrate = `64`;\n break;\n }\n result = await this.atracdencProcess!.encode(data.buffer, bitrate);\n }\n this.ffmpegProcess.worker.terminate();\n this.atracdencProcess!.terminate();\n return result;\n }\n}\n","/* eslint no-restricted-globals: 0 */\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport * as serviceWorker from './serviceWorker';\nimport { NetMDUSBService } from './services/netmd';\nimport { NetMDMockService } from './services/netmd-mock';\nimport serviceRegistry from './services/registry';\n\nimport { store } from './redux/store';\nimport { actions as appActions } from './redux/app-feature';\n\nimport App from './components/app';\n\nimport './index.css';\nimport { FFMpegAudioExportService } from './services/audio-export';\nimport { MediaRecorderService } from './services/mediarecorder';\n\nserviceRegistry.netmdService = new NetMDUSBService({ debug: true });\n// serviceRegistry.netmdService = new NetMDMockService(); // Uncomment to work without a device attached\nserviceRegistry.audioExportService = new FFMpegAudioExportService();\nserviceRegistry.mediaRecorderService = new MediaRecorderService();\n\n(function setupEventHandlers() {\n window.addEventListener('beforeunload', ev => {\n let isUploading = store.getState().uploadDialog.visible;\n if (!isUploading) {\n return;\n }\n ev.preventDefault();\n ev.returnValue = `Warning! Recording will be interrupted`;\n });\n\n if (navigator && navigator.usb) {\n navigator.usb.ondisconnect = function() {\n store.dispatch(appActions.setState('WELCOME'));\n };\n } else {\n store.dispatch(appActions.setBrowserSupported(false));\n }\n\n // eslint-disable-next-line\n let deferredPrompt: any;\n window.addEventListener('beforeinstallprompt', (e: any) => {\n e.preventDefault();\n deferredPrompt = e;\n });\n})();\n\nReactDOM.render(\n \n \n ,\n document.getElementById('root')\n);\n\n// serviceWorker.unregister();\nserviceWorker.register();\n","import { sanitizeTitle, getPublicPathFor } from '../utils';\nimport Recorder from 'recorderjs';\n\nexport class MediaRecorderService {\n public recorder: any;\n public stream?: MediaStream;\n public audioContext?: AudioContext;\n public analyserNode?: AnalyserNode;\n public gainNode?: GainNode;\n\n playTestInput(deviceId: string) {\n this.audioContext = new AudioContext();\n this.gainNode = this.audioContext.createGain();\n this.analyserNode = this.audioContext.createAnalyser();\n\n this.initStream(deviceId).then(() => {\n const source = this.audioContext!.createMediaStreamSource(this.stream!);\n source.connect(this.gainNode!);\n this.gainNode!.connect(this.analyserNode!);\n this.analyserNode!.connect(this.audioContext!.destination);\n });\n }\n\n stopTestInput() {\n if (!this.audioContext) {\n return;\n }\n this.audioContext?.close();\n delete this.audioContext;\n this.closeStream();\n }\n\n async initStream(deviceId: string) {\n const recordConstraints = {\n // Try to set the best recording params for ripping the audio tracks\n autoGainControl: false,\n channelCount: 2,\n deviceId: deviceId,\n echoCancellation: false,\n noiseSuppression: false,\n sampleRate: 44100,\n highpassFilter: false,\n };\n this.stream = await navigator.mediaDevices.getUserMedia({ audio: recordConstraints });\n\n // Dump recording settings\n const audioTracks = this.stream.getAudioTracks();\n if (audioTracks.length > 0) {\n console.log('Record Setings:', audioTracks[0].getSettings());\n }\n }\n\n async startRecording() {\n this.audioContext = new AudioContext();\n const input = this.audioContext.createMediaStreamSource(this.stream!);\n this.recorder = new Recorder(input, { workerPath: getPublicPathFor(`recorderWorker.js`) });\n this.recorder.record();\n }\n\n async stopRecording() {\n this.recorder.stop();\n }\n\n async closeStream() {\n this.stream?.getTracks().forEach(track => track.stop());\n }\n\n downloadRecorded(title: string) {\n this.recorder.exportWAV((buffer: Blob) => {\n let url = URL.createObjectURL(buffer);\n let a = document.createElement('a');\n document.body.appendChild(a);\n a.style.display = 'none';\n a.href = url;\n a.download = `${sanitizeTitle(title)}.wav`;\n a.click();\n window.URL.revokeObjectURL(url);\n document.body.removeChild(a);\n });\n }\n}\n"],"sourceRoot":""}